home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / folder.c < prev    next >
C/C++ Source or Header  |  1996-05-14  |  142KB  |  5,070 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: folder.c,v 4.231 1996/05/15 01:23:36 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    USENET News reading additions in part by L Lundblade / NorthWestNet, 1993
  32.    lgl@nwnet.net
  33.  
  34.    Pine is in part based on The Elm Mail System:
  35.     ***********************************************************************
  36.     *  The Elm Mail System  -  Revision: 2.13                             *
  37.     *                                                                     *
  38.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  39.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  40.     ***********************************************************************
  41.  
  42.  
  43.   ----------------------------------------------------------------------*/
  44.  
  45. /*======================================================================
  46.      folder.c
  47.  
  48.   Screen to display and manage all the users folders
  49.  
  50. This puts up a list of all the folders in the users mail directory on
  51. the screen spacing it nicely. The arrow keys move from one to another
  52. and the user can delete the folder or select it to change to or copy a
  53. message to. The dispay lets the user scroll up or down a screen full,
  54. or search for a folder name.
  55.  ====*/
  56.  
  57.  
  58. #include "headers.h"
  59.  
  60. #define    CLICKHERE    "[ Select Here to See Expanded List ]"
  61. #define    CLICKHERETOO    "[ ** Empty List **  Select Here to Try Re-Expanding ]"
  62. #define    CLICKHERETOONEWS \
  63.     "[ ** Empty List **  Use \"A Subscribe\" to subscribe to a newsgroup ]"
  64. #define    ALL_FOUND(X)    (((X)->use&CNTXT_NOFIND) == 0 && \
  65.               ((X)->use&CNTXT_PARTFIND) == 0)
  66. #define    FLDR_NAME(X)    ((X) ? ((X)->nickname ? (X)->nickname : (X)->name) :"")
  67. #define    SUBSCRIBE_PMT    \
  68.                "Enter newsgroup name (or partial name to get a list): "
  69.     
  70.  
  71. /*----------------------------------------------------------------------
  72.    The data needed to redraw the folders screen, including the case where the 
  73. screen changes size in which case it may recalculate the folder_display.
  74.   ----*/
  75.  
  76. /* BUG: this strategy is doomed to fail when resize happens during 
  77.  *      printing that refigures line numbers and such as they're
  78.  *      stored with actual folder data (ie only one postition at a time).
  79.  */
  80. typedef struct folder_screen_state
  81. {
  82.     CONTEXT_S *context_list;        /* list of context's to display */
  83.     CONTEXT_S *context;            /* current context              */
  84.     int        folder_index;        /* current index in context     */
  85.     CONTEXT_S *prev_context;        /* previous context              */
  86.     int        prev_index;        /* perv index in prev context     */
  87.     int        display_cols;        /* number of columns on display   */
  88.     int        display_rows;        /* number of rows on display      */
  89.     int           top_row;            /* folder list row at top left    */
  90.     int        last_row;        /* last row of folder list        */
  91. } FSTATE_S;
  92.  
  93. static FSTATE_S *fs;
  94.  
  95. typedef enum {NotChecked, NotInCache, Found, Missing, End} NgCacheReturns;
  96.  
  97.  
  98. /*
  99.  * Global's used by context_mailbox and context_bboard to tie foldernames
  100.  * returned by c-client into the proper folder list
  101.  */
  102. extern void       *find_folder_list;
  103. extern MAILSTREAM *find_folder_stream;
  104. extern long        find_folder_count;
  105. extern int       find_folder_inbox;
  106. #ifdef NEWBB
  107. extern void       *newbb_folder_list;
  108. #endif
  109.  
  110.  
  111. /* short definition to keep compilers happy */
  112. typedef    int (*QSFunc) PROTO((const QSType *, const QSType *));
  113.  
  114.  
  115. /*
  116.  * Internal prototypes
  117.  */
  118. void       redraw_folder_screen PROTO(());
  119. void       display_folder PROTO((FSTATE_S *, CONTEXT_S *, int, CONTEXT_S *, \
  120.                  int));
  121. int       folder_scroll_up PROTO((long));
  122. int       folder_scroll_down PROTO((long));
  123. int       folder_scroll_to_pos PROTO((long));
  124. void       paint_folder_name PROTO((int, FSTATE_S *, int, CONTEXT_S *));
  125. char      *folder_list_entry PROTO((FSTATE_S *, int, int *, CONTEXT_S **));
  126. void       folder_insert_index PROTO((FOLDER_S *, int, void *));
  127. int        folder_total PROTO((void *));
  128. int        off_folder_display PROTO((FSTATE_S *, int, CONTEXT_S *));
  129. char      *add_new_folder PROTO((int, CONTEXT_S *));
  130. char      *group_subscription PROTO((int, CONTEXT_S *));
  131. char      *rename_folder PROTO((int, int, CONTEXT_S *));
  132. int        delete_folder PROTO((int, CONTEXT_S *, int *));
  133. void       print_folders PROTO((FSTATE_S *));
  134. int        search_folders PROTO((FSTATE_S *, int));
  135. int        compare_names PROTO((const QSType *, const QSType *));
  136. int        compare_sizes PROTO((const QSType *, const QSType *));
  137. int        compare_folders PROTO((const QSType *, const QSType *));
  138. int        compare_folders_new PROTO((const QSType *, const QSType *));
  139. void       create_folder_display PROTO((FSTATE_S *, int));
  140. void      *new_folder_list PROTO(());
  141. void       free_folder_list PROTO((void **));
  142. void      *find_folder_names PROTO((char *, char *));
  143. void       free_folders_in_context PROTO((CONTEXT_S *));
  144. FOLDER_S  *new_folder PROTO((char *));
  145. void       folder_delete PROTO((int, void *));
  146. void       update_news_prefix PROTO((MAILSTREAM *,  struct folder *));
  147. char      *get_post_list PROTO((char **));
  148. int        sort_folder_list PROTO((void  *, QSFunc));
  149. int       folder_has_recent PROTO((MAILSTREAM **, CONTEXT_S *, FOLDER_S *));
  150. NgCacheReturns chk_newsgrp_cache PROTO((char *));
  151. void       add_newsgrp_cache PROTO((char *, NgCacheReturns));
  152. #ifdef    _WINDOWS
  153. int       folder_scroll_callback PROTO((int,long));
  154. #endif
  155. #ifdef NEWBB
  156. void       clear_new_groups PROTO((void *));
  157. #endif
  158.  
  159.  
  160.  
  161. static struct key folder_keys[] =
  162.        {{"?","Help",KS_SCREENHELP},      {"O","OTHER CMDS",KS_NONE},
  163.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  164.     {"P","PrevFldr",KS_NONE},    {"N","NextFldr",KS_NONE},
  165.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  166.     {"D","Delete",KS_NONE},        {"A","Add",KS_NONE},
  167.     {"R","Rename",KS_NONE},        {"W","WhereIs",KS_NONE},
  168.  
  169.     {"?","Help",KS_NONE},        {"O","OTHER CMDS",KS_NONE},
  170.     {"Q","Quit",KS_EXIT},        {"C","Compose",KS_COMPOSER},
  171.     {NULL,NULL,KS_NONE},        {"G","GotoFldr",KS_GOTOFLDR},
  172.     {"I","CurIndex",KS_FLDRINDEX},    {"W","WhereIs",KS_WHEREIS},
  173.     {"Y","prYnt",KS_PRINT},        {NULL,NULL,KS_NONE},
  174.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE}};
  175. INST_KEY_MENU(folder_keymenu, folder_keys);
  176. #define    MAIN_KEY      2  /* Sometimes Main, sometimes Exit */
  177. #define    SELECT_KEY    3  /* Sometimes View, sometimes Select */
  178. #define    OTHER_KEY    1
  179. #define    DELETE_KEY    8
  180. #define    ADD_KEY            9
  181. #define    RENAME_KEY    10
  182. #define    WHEREIS_KEY    11
  183.  
  184.  
  185.  
  186. /*----------------------------------------------------------------------
  187.       Front end to folder lister when it's called from the main menu
  188.  
  189.  Args: pine_state -- The general pine_state data structure
  190.  
  191.  Result: runs folder_lister
  192.  
  193.   ----*/
  194. void
  195. folder_screen(pine_state)
  196.     struct pine *pine_state;
  197. {
  198.     dprint(1, (debugfile, "=== folder_screen called ====\n"));
  199.     mailcap_free(); /* free resources we won't be using for a while */
  200.     pine_state->next_screen = main_menu_screen;
  201.     folder_lister(pine_state, FolderMaint, NULL, NULL, NULL, NULL,
  202.                   pine_state->context_list, NULL);
  203.     pine_state->prev_screen = folder_screen;
  204. }
  205.  
  206.  
  207.  
  208. /*----------------------------------------------------------------------
  209.          Browse folders for ^T selection from the composer
  210.   
  211.  Args: error_mess -- pointer to place to return an error message
  212.   
  213.  Returns: result if folder selected, NULL if not
  214.       Composer expects the result to be alloc'd here 
  215.  
  216.   ----*/     
  217. char *
  218. folders_for_fcc(error_mess)
  219.      char **error_mess;
  220. {
  221.     char       tmp[MAXPATH], *rs = NULL;
  222.     int        rv;
  223.     CONTEXT_S *cntxt = ps_global->context_current;
  224.  
  225.     /* Coming back from composer */
  226.     fix_windsize(ps_global);
  227.     init_sigwinch();
  228.  
  229.     /*
  230.      * if folder_lister returns ambiguous name IF the chosen context
  231.      * matches the default save context, OTHERWISE it returns the 
  232.      * selected folder's fully qualified name.
  233.      *
  234.      * Note: this could stand to be cleaned up.  That is, it would be
  235.      * nice for the user to be shown the context along with the 
  236.      * folder name rather than the raw IMAP name.  Perhaps, someday
  237.      * adding understanding of nicknames perhaps with <nickname>folder
  238.      * syntax similar to the titlebar makes sense...
  239.      */
  240.     if(rv = folder_lister(ps_global, GetFcc, NULL, &cntxt, tmp, NULL,
  241.               ps_global->context_list, NULL)){
  242.     if(context_isambig(tmp) && !((cntxt->use) & CNTXT_SAVEDFLT)){
  243.         context_apply(rs = tmp_20k_buf, cntxt->context, tmp);
  244.  
  245.         /*
  246.          * A bit of a problem here.  If the foldername applied
  247.          * to the context is still relative, we can probably assume 
  248.          * the folder's relative to ~, so add it here for now.
  249.          * NOTE: this WILL break if a local driver is added, that
  250.          * isn't file system based...
  251.          */
  252.         if(context_isambig(rs))
  253.           build_path(rs = tmp, ps_global->ui.homedir, tmp_20k_buf);
  254.     }
  255.     else
  256.       rs = tmp;
  257.     }
  258.  
  259.     return(rs ? cpystr(rs) : NULL);
  260. }
  261.  
  262.     
  263.  
  264. /*----------------------------------------------------------------------
  265.       Main routine for the list of folders screen, displays and execute cmds.
  266.  
  267.   Args:  ps            -- The pine_state data structure
  268.          do_what       -- What function we're called as -- select, maint...
  269.          return_string -- Buffer to return selected folder name in
  270.          return_array  -- Return *alloced* array of selected folder names here
  271.      return_context -- Context that the return_string makes is applied to
  272.      start_context -- Context to first display
  273.  
  274.   Result: return 0 for abort, 1 for open folder, and 2 for save folder
  275.           The name selected is copied into the given buffer
  276.  
  277. This code assume that the folder list will have one folder in it and
  278. is likey to crash if there isn't one.
  279.  
  280.   ----*/
  281. int 
  282. folder_lister(ps, do_what, start_context, return_context, return_string,
  283.               return_array, context_list, f_state)
  284.     struct pine *ps;
  285.     FolderFun    do_what;
  286.     CONTEXT_S   *start_context;
  287.     CONTEXT_S  **return_context;
  288.     CONTEXT_S   *context_list;
  289.     char        *return_string;
  290.     char      ***return_array;
  291.     void        *f_state;
  292. {
  293.     int              ch, orig_ch, mangled_footer, mangled_header, km_size,
  294.                      rv, quest_line, rc, cur_row, cur_col, km_popped,
  295.              doing_listmode;
  296.     unsigned short   new_col;
  297.     CONTEXT_S       *tc;
  298.     char            *new_file, *cur_name, *new_fold;
  299.     FOLDER_S        *cur_f;
  300.     struct key_menu *km;
  301.     bitmap_t         bitmap;
  302.     OtherMenu        what;
  303.     FSTATE_S         default_fstate;
  304.  
  305.     dprint(1, (debugfile, "\n\n    ---- FOLDER SCREEN ----\n"));
  306.  
  307.     fs                 = f_state ? (FSTATE_S *)f_state : &default_fstate;
  308.     quest_line         = -FOOTER_ROWS(ps);
  309.     fs->prev_index     = -1;
  310.     fs->prev_context   = NULL;
  311.     fs->top_row           = 0;
  312.     mangled_footer     = 1;
  313.     mangled_header     = 1;
  314.     what               = FirstMenu;
  315.     km_popped          = 0;
  316.     doing_listmode     = 0;
  317.     fs->context_list   = context_list;
  318.     fs->context        = start_context ? start_context 
  319.                        : (ps->context_current) 
  320.                      ? ps->context_current
  321.                      : context_list;
  322.  
  323.     /*
  324.      * The subscription handler's already done the find, so
  325.      * just expand if it's the default, there's only one context,
  326.      * or we're here to select something...
  327.      */
  328.     if(do_what != Subscribe){
  329.     if(F_ON(F_EXPANDED_FOLDERS,ps_global) || !context_list->next){
  330.         for(tc = context_list; tc ; tc = tc->next)
  331.           find_folders_in_context(NULL, tc, NULL);
  332.     }
  333.     else if(do_what != FolderMaint && do_what != Subscribe)
  334.       find_folders_in_context(NULL, fs->context, NULL);
  335.     }
  336.  
  337.     if(do_what == Subscribe || do_what == PostNews
  338.        || (fs->folder_index = folder_index(ps->cur_folder, 
  339.                        fs->context->folders)) < 0)
  340.       fs->folder_index = 0;
  341.  
  342.     ClearBody();
  343.  
  344.     create_folder_display(fs, ps->ttyo->screen_cols);
  345.  
  346.     ps->redrawer = redraw_folder_screen;
  347.  
  348.     for(ch = 'y' /* For display_message first time through */;;) {
  349.  
  350.     if(km_popped){
  351.         km_popped--;
  352.         if(km_popped == 0){
  353.         clearfooter(ps);
  354.         fs->prev_index = -1;
  355.         }
  356.     }
  357.  
  358.         /*------------ New mail check ----------*/
  359.         if(ps->ttyo->screen_rows > 1 && new_mail(0, NM_TIMING(ch), 1) >= 0)
  360.       mangled_header = 1;
  361.  
  362.         if(streams_died())
  363.           mangled_header = 1;
  364.  
  365.         /*----------  screen painting -------------*/
  366.     if(mangled_header && ps->ttyo->screen_rows > 0) {
  367.         mangled_header = 0;
  368.             switch(do_what) {
  369.               case FolderMaint:
  370.             set_titlebar("FOLDER LIST", ps->mail_stream,
  371.                  ps->context_current,ps->cur_folder,
  372.                  ps->msgmap, 1, FolderName, 0, 0);
  373.                 break;
  374.  
  375.               case OpenFolder:
  376.             set_titlebar("GOTO: SELECT FOLDER", ps->mail_stream,
  377.                  ps->context_current, ps->cur_folder,
  378.                  ps->msgmap, 1, FolderName, 0, 0);
  379.                 break;
  380.  
  381.               case SaveMessage:
  382.             set_titlebar("SAVE: SELECT FOLDER", ps->mail_stream,
  383.                  ps->context_current, ps->cur_folder, ps->msgmap,
  384.                  1, MessageNumber, 0, 0);
  385.                 break;
  386.  
  387.               case GetFcc:
  388.                 set_titlebar("FCC: SELECT FOLDER", ps->mail_stream,
  389.                  ps->context_current, ps->cur_folder, ps->msgmap,
  390.                  1, FolderName, 0, 0);
  391.                 break;
  392.  
  393.               case Subscribe:
  394.                 set_titlebar("SUBSCRIBE: SELECT FOLDER", ps->mail_stream,
  395.                  ps->context_current, ps->cur_folder, ps->msgmap,
  396.                  1, FolderName, 0, 0);
  397.                 break;
  398.  
  399.               case PostNews:
  400.                 set_titlebar("NWSGRP: SELECT GROUP", ps->mail_stream,
  401.                  ps->context_current, ps->cur_folder, ps->msgmap,
  402.                  1, FolderName, 0, 0);
  403.                 break;
  404.             }
  405.     }
  406.  
  407.     /*
  408.      * display_folders handles all display painting.
  409.      * only the first time thru (or if screen parms change)
  410.      * do we need to explicitly tell it to redraw.  Otherwise
  411.      * it handles framing the page and highlighting the current
  412.      * folder...
  413.      */
  414.     if(fs->prev_index != fs->folder_index
  415.        || fs->prev_context != fs->context
  416.        || (do_what == Subscribe && ch == 'x')
  417.        || (do_what == Subscribe && doing_listmode
  418.            && (ch == ctrl('M') || ch == ctrl('J') || ch == KEY_MOUSE)))
  419.       display_folder(fs, fs->context, fs->folder_index,
  420.              fs->prev_context, fs->prev_index);
  421.  
  422.     if(fs->prev_context && fs->prev_context != fs->context){
  423.         q_status_message1(SM_ORDER, 0, 3, "Now in collection <%s>", 
  424.                   fs->context->label[0]);
  425.  
  426.         if(do_what == FolderMaint && (fs->prev_context 
  427.            && ((fs->prev_context->use&CNTXT_PSEUDO)
  428.                != (fs->context->use&CNTXT_PSEUDO))))
  429.           mangled_footer++;
  430.             if((fs->prev_context->type & FTYPE_BBOARD) ^
  431.                (fs->context->type & FTYPE_BBOARD))
  432.               mangled_footer++;
  433.     }
  434.  
  435.     if(mangled_footer && ps->ttyo->screen_rows > HEADER_ROWS(ps) + 1) {
  436.         setbitmap(bitmap);
  437.         km = &folder_keymenu;
  438.             folder_keys[DELETE_KEY].name = "D";
  439.             folder_keys[ADD_KEY].name = "A";
  440.             folder_keys[RENAME_KEY].name = "R";
  441.         folder_keys[RENAME_KEY].label = "Rename";
  442.             if(fs->context->type & FTYPE_BBOARD) {
  443.                 folder_keys[ADD_KEY].label = "Subscribe";
  444.                 folder_keys[DELETE_KEY].label = "UnSbscrbe";
  445.         KS_OSDATASET(&folder_keys[DELETE_KEY], KS_NONE);
  446.             } else {
  447.                 folder_keys[ADD_KEY].label = "Add";
  448.                 folder_keys[DELETE_KEY].label = "Delete";
  449.         KS_OSDATASET(&folder_keys[DELETE_KEY], KS_NONE);
  450.             }
  451.         if(do_what == FolderMaint){
  452.           km->how_many = 2;
  453.           folder_keys[MAIN_KEY].name = "M";
  454.           folder_keys[MAIN_KEY].label = "Main Menu";
  455.           KS_OSDATASET(&folder_keys[MAIN_KEY], KS_MAINMENU);
  456.           folder_keys[SELECT_KEY].name = "V";
  457.           folder_keys[SELECT_KEY].label = 
  458.            (fs->context->use & CNTXT_PSEUDO) ? "[Select]":"[ViewFldr]";
  459.           KS_OSDATASET(&folder_keys[SELECT_KEY], KS_NONE);
  460.           clrbitn(WHEREIS_KEY, bitmap); /* the one in the 1st menu */
  461.         }
  462.         else {
  463.           km->how_many = 1;
  464.           folder_keys[MAIN_KEY].name = "E";
  465.           folder_keys[MAIN_KEY].label = do_what != Subscribe ? 
  466.                                                "ExitSelect" : "ExitSubscb";
  467.  
  468.           folder_keys[SELECT_KEY].name = "S";
  469.           folder_keys[SELECT_KEY].label = do_what != Subscribe ?
  470.                                                "[Select]" : "[Subscribe]";
  471.           KS_OSDATASET(&folder_keys[SELECT_KEY], KS_NONE);
  472.           clrbitn(OTHER_KEY, bitmap);
  473.           clrbitn(RENAME_KEY, bitmap);
  474.           if(do_what == Subscribe){
  475.           if(doing_listmode){
  476.               folder_keys[DELETE_KEY].name = "X";
  477.               folder_keys[DELETE_KEY].label = "[Set/Unset]";
  478.               folder_keys[SELECT_KEY].label = "Subscribe";
  479.           }
  480.           else{
  481.               folder_keys[DELETE_KEY].name = "L";
  482.               folder_keys[DELETE_KEY].label = "ListMode";
  483.           }
  484.           }
  485.           else
  486.             clrbitn(DELETE_KEY, bitmap);
  487.  
  488. #ifdef NEWBB
  489.               if(do_what == Subscribe) {
  490.                   folder_keys[ADD_KEY].name = "X";
  491.                   folder_keys[ADD_KEY].label = "DismissNew";
  492.               } else {
  493.                clrbitn(ADD_KEY, bitmap);
  494.               }
  495. #else
  496.           clrbitn(ADD_KEY, bitmap);
  497. #endif
  498.  
  499.         }
  500.  
  501.         if(km_popped){
  502.         FOOTER_ROWS(ps) = 3;
  503.         clearfooter(ps);
  504.         }
  505.  
  506.         draw_keymenu(km, bitmap, ps->ttyo->screen_cols,
  507.         1-FOOTER_ROWS(ps), 0, what, 0);
  508.         mangled_footer = 0;
  509.         what           = SameTwelve;
  510.         if(km_popped){
  511.         FOOTER_ROWS(ps) = 1;
  512.         mark_keymenu_dirty();
  513.         }
  514.     }
  515.  
  516.         fs->prev_index   = fs->folder_index;
  517.         fs->prev_context = fs->context;
  518.     if(folder_total(fs->context->folders)){
  519.         cur_f = folder_entry(fs->folder_index, fs->context->folders);
  520.         cur_name = FLDR_NAME(cur_f);
  521.     }
  522.     else
  523.      cur_f  = NULL;
  524.  
  525.         /*------- display any status messages -----*/
  526.     if(km_popped){
  527.         FOOTER_ROWS(ps) = 3;
  528.         mark_status_unknown();
  529.     }
  530.  
  531.     display_message(ch);
  532.     if(km_popped){
  533.         FOOTER_ROWS(ps) = 1;
  534.         mark_status_unknown();
  535.     }
  536.  
  537.     if(F_ON(F_SHOW_CURSOR, ps) && cur_f){
  538.         cur_row = HEADER_ROWS(ps) + (cur_f->d_line - fs->top_row);
  539.         cur_col = cur_f->d_col;
  540.     }
  541.     else{
  542.         cur_row = ps->ttyo->screen_rows - FOOTER_ROWS(ps);
  543.         cur_col = 0;
  544.     }
  545.  
  546.         /*----- Read and validate the next command ------*/
  547.     MoveCursor(cur_row, cur_col);
  548.  
  549. #ifdef    MOUSE
  550.     mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);/* prime the handler */
  551.     register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
  552.               ps_global->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
  553.               ps_global->ttyo->screen_cols);
  554. #endif
  555. #ifdef    _WINDOWS
  556.     mswin_setscrollcallback(folder_scroll_callback);
  557. #endif
  558.     ch = read_command();
  559. #ifdef    MOUSE
  560.     clear_mfunc(mouse_in_content);
  561. #endif
  562. #ifdef    _WINDOWS
  563.     mswin_setscrollcallback(NULL);
  564. #endif
  565.         orig_ch = ch;
  566.         
  567.     if(ch < 'z' && isupper((unsigned char)ch))
  568.       ch = tolower((unsigned char)ch);
  569.  
  570.     if(km->which == 1)
  571.           if(ch >= PF1 && ch <= PF12)
  572.             ch = PF2OPF(ch);
  573.  
  574.     ch = validatekeys(ch);
  575.  
  576.         dprint(5, (debugfile, "folder command: %c (%d)\n",ch,ch));
  577.  
  578.     if(km_popped)
  579.       switch(ch){
  580.         case NO_OP_IDLE:
  581.         case NO_OP_COMMAND: 
  582.         case PF2:
  583.         case OPF2:
  584.         case 'o' :
  585.         case KEY_RESIZE:
  586.         case ctrl('L'):
  587.           km_popped++;
  588.           break;
  589.         
  590.         default:
  591.           clearfooter(ps);
  592.           break;
  593.       }
  594.  
  595.         /*----------- Execute command --------------*/
  596.     switch(ch) {
  597.  
  598.             /*---------- display other key bindings ------*/
  599.           case PF2:
  600.           case OPF2:
  601.           case 'o' :
  602.             if(do_what != FolderMaint)
  603.               goto bleep;
  604.  
  605.             if (ch == 'o')
  606.           warn_other_cmds();
  607.             what = NextTwelve;
  608.             mangled_footer++;
  609.             break;
  610.  
  611.  
  612.             /*---------------------- Key left --------------*/
  613.       case ctrl('B'):
  614.       case KEY_LEFT:
  615.       case PF5:
  616.       case 'p':
  617.         if(fs->folder_index > 0 && ALL_FOUND(fs->context)){
  618.         fs->folder_index--;
  619.         }
  620.         else if(fs->context_list != fs->context){
  621.         for(tc = fs->context_list; tc->next != fs->context;
  622.             tc = tc->next);
  623.  
  624.         fs->context = tc;
  625.         if(ALL_FOUND(fs->context) && folder_total(fs->context->folders))
  626.           fs->folder_index = folder_total(fs->context->folders) - 1;
  627.         else
  628.           fs->folder_index = 0;
  629.         }
  630.         else
  631.           q_status_message(SM_ORDER,0,1,"Already on first folder.");
  632.  
  633.         break;
  634.  
  635.  
  636.             /*--------------------- Key right -------------------*/
  637.           case ctrl('F'): 
  638.           case KEY_RIGHT:
  639.       case PF6:
  640.       case 'n':
  641.           case '\t':
  642.         if(fs->folder_index + 1 < folder_total(fs->context->folders)
  643.            && ALL_FOUND(fs->context)){
  644.         fs->folder_index++;
  645.         }
  646.         else if(fs->context->next){    /* next context?*/
  647.         fs->context = fs->context->next;
  648.         fs->folder_index = 0;
  649.         }
  650.         else
  651.           q_status_message(SM_ORDER,0,1,"Already on last folder.");
  652.  
  653.             break;
  654.  
  655.  
  656.             /*--------------- Key up ---------------------*/
  657.           case KEY_UP:
  658.       case ctrl('P'):
  659.         if(!folder_total(fs->context->folders))
  660.           break;
  661.  
  662.         new_col = folder_entry(fs->folder_index, 
  663.                    fs->context->folders)->d_col;
  664.         rc        = folder_entry(fs->folder_index, 
  665.                    fs->context->folders)->d_line - 1;
  666.  
  667.         /* find next line */
  668.         while(rc >= 0 && folder_list_entry(fs, rc, &(fs->folder_index),
  669.                            &(fs->context)))
  670.           rc--;
  671.  
  672.         if(rc < 0){
  673.         q_status_message(SM_ORDER, 0, 1, "Already on first line.");
  674.         if(fs->top_row != 0){        /* make sure! */
  675.             fs->top_row    = 0;
  676.             fs->prev_index = -1;
  677.         }
  678.  
  679.         break;
  680.         }
  681.  
  682.         /* find the right column on it */
  683.         while((cur_f = folder_entry(fs->folder_index, 
  684.                      fs->context->folders))->d_col < new_col){
  685.         if(fs->folder_index+1 >= folder_total(fs->context->folders)
  686.            || folder_entry(fs->folder_index + 1,
  687.                    fs->context->folders)->d_col == 0)
  688.           break;
  689.         else
  690.           fs->folder_index++;
  691.         }
  692.  
  693.         if((rv = fs->top_row - cur_f->d_line + HS_MARGIN(ps)) > 0){
  694.         int i;
  695.  
  696.         for(i=cur_f->d_line-1, rc=fs->folder_index, tc=fs->context;
  697.             i >= 0 && folder_list_entry(fs, i, &rc, &tc);
  698.             i--, rv++)
  699.           ;
  700.  
  701.         folder_scroll_down(rv);
  702.         fs->prev_index = -1;
  703.         }
  704.  
  705.             break;
  706.  
  707.  
  708.             /*----------------- Key Down --------------------*/
  709.           case KEY_DOWN:
  710.       case ctrl('N'):
  711.         if(!folder_total(fs->context->folders))
  712.           break;
  713.  
  714.         new_col = folder_entry(fs->folder_index, 
  715.                    fs->context->folders)->d_col;
  716.         rc      = folder_entry(fs->folder_index, 
  717.                    fs->context->folders)->d_line + 1;
  718.  
  719.         if(rc > fs->last_row){
  720.         q_status_message(SM_ORDER, 0, 1, "Already on last line.");
  721.         break;
  722.         }
  723.  
  724.         /* find next line */
  725.         while(rc <= fs->last_row
  726.           && folder_list_entry(fs, rc, &(fs->folder_index),
  727.                        &(fs->context)))
  728.           rc++;
  729.  
  730.         /* find the right column on it */
  731.         while(folder_entry(fs->folder_index,
  732.                    fs->context->folders)->d_col < new_col){
  733.         if(fs->folder_index+1 >= folder_total(fs->context->folders)
  734.            || folder_entry(fs->folder_index + 1, 
  735.                    fs->context->folders)->d_col == 0)
  736.           break;
  737.         else
  738.           fs->folder_index++;
  739.         }
  740.  
  741.         if((rc = folder_entry(fs->folder_index,
  742.                   fs->context->folders)->d_line
  743.                   - (fs->top_row + fs->display_rows
  744.                      - HS_MARGIN(ps)) + 1) > 0){
  745.         fs->prev_index = -1;
  746.         folder_scroll_up(rc);
  747.         }
  748.  
  749.         break;
  750.  
  751.  
  752.             /*----------------- Beginning of Line --------------------*/
  753.       case ctrl('A'):
  754.         while(1){
  755.         if(fs->folder_index == 0
  756.            || folder_entry(fs->folder_index, 
  757.                    fs->context->folders)->d_col == 0)
  758.           break;
  759.         else
  760.           fs->folder_index--;
  761.         }
  762.  
  763.         break;
  764.  
  765.  
  766.             /*----------------- End of Line --------------------*/
  767.       case ctrl('E'):
  768.         while(1){
  769.         if(fs->folder_index+1 >= folder_total(fs->context->folders)
  770.            || folder_entry(fs->folder_index + 1, 
  771.                    fs->context->folders)->d_col == 0)
  772.           break;
  773.         else
  774.           fs->folder_index++;
  775.         }
  776.  
  777.         break;
  778.  
  779.  
  780. #ifdef    MOUSE
  781.             /*-------------- Mouse down in content ----------------------*/
  782.       case KEY_MOUSE:
  783.         {
  784.         MOUSEPRESS mp;
  785.         int new_index, ncol, tcol;
  786.         CONTEXT_S    *new_context;
  787.  
  788.         mouse_get_last (NULL, &mp);
  789.         mp.row -= HEADER_ROWS(ps);
  790.  
  791.         /* Mouse down below last clickable line? */
  792.         if (mp.row > fs->last_row)
  793.           break;
  794.  
  795.         /* Clicked on one of those seperator lines? */
  796.         if (folder_list_entry(fs, mp.row + fs->top_row,
  797.                       &new_index, &new_context))
  798.           break;
  799.  
  800.         /* folder_list_entry should have given us a new context
  801.          * and index for first folder on line.  Find folder under
  802.          * the mouse click by scanning across the line. */
  803.         while((tcol = folder_entry(new_index, 
  804.                 new_context->folders)->d_col) < mp.col) {
  805.             /* Reached end of folders?  */
  806.             if (new_index+1 >= folder_total(new_context->folders))
  807.               break;
  808.             /* Reached end of line? */
  809.             if ((ncol = folder_entry(new_index + 1, 
  810.                     new_context->folders)->d_col) == 0)
  811.               break;
  812.             /* Mouse click with in column range? */
  813.             if (mp.col >= tcol && mp.col < ncol)
  814.               break;
  815.             ++new_index;
  816.         }
  817.  
  818.         /*
  819.          * On single click we move to new item.
  820.          * On double click we test that we are on highlighted item
  821.          * and the take action on it.
  822.          */
  823.         if (!mp.doubleclick) {
  824.             fs->folder_index = new_index;
  825.             fs->context = new_context;
  826.         }
  827.         else {
  828.             if(do_what == Subscribe && doing_listmode){
  829.             goto DoToggle;  /* different default in this case */
  830.             }
  831.             else if(fs->folder_index == new_index
  832.                 && fs->context == new_context) 
  833.               goto DoSelect;
  834.         }
  835.         }
  836.         break;
  837. #endif    /* MOUSE */
  838.  
  839.  
  840.             /*--------------Scroll Up ----------------------*/
  841.           case PF7: 
  842.       case KEY_PGUP:
  843.           case ctrl('Y'): 
  844.       case '-':
  845.         if(fs->top_row
  846.         || (fs->folder_index > 0 && ALL_FOUND(fs->context))
  847.             || (fs->context_list != fs->context)){
  848.         rc = max(0, fs->top_row - fs->display_rows);
  849.  
  850.         while(folder_list_entry(fs, rc, &(fs->folder_index), 
  851.                     &(fs->context)))
  852.               rc++;
  853.         } else {
  854.                 q_status_message(SM_ORDER,0,1,"Already on first page.");
  855.         }
  856.  
  857.             break;
  858.  
  859.  
  860.             /*---------- Scroll screenful ------------*/
  861.       case PF8:
  862.       case KEY_PGDN:
  863.       case SPACE: 
  864.           case ctrl('V'): 
  865.       case '+':
  866.         if(!folder_total(fs->context->folders))
  867.           break;
  868.  
  869.         if((rc = fs->top_row + fs->display_rows) > fs->last_row){
  870.         if((int)folder_entry(fs->folder_index,
  871.                 fs->context->folders)->d_line >= fs->last_row){
  872.             q_status_message(SM_ORDER,0,1,"Already on last page.");
  873.             break;
  874.         }
  875.         else
  876.           rc = fs->last_row;
  877.         }
  878.  
  879.         while(rc <= fs->last_row
  880.           && folder_list_entry(fs, rc, &(fs->folder_index),
  881.                        &(fs->context)))
  882.           rc++;
  883.  
  884.         break;
  885.  
  886.  
  887.             /*------------------ Help ----------------------*/
  888.       case PF1:
  889.       case OPF1:
  890.       case '?':
  891.       case ctrl('G'):
  892.         if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
  893.         km_popped = 2;
  894.         mangled_footer = 1;
  895.         break;
  896.         }
  897.  
  898.             ps_global->next_screen = SCREEN_FUN_NULL;
  899.             ps_global->redrawer = (void (*)())NULL;
  900.         km_size = FOOTER_ROWS(ps_global);
  901.             switch(do_what) {
  902.               case FolderMaint:
  903.                 helper(h_folder_maint, "HELP FOR FOLDERS", 0);
  904.                 break;
  905.               case OpenFolder:
  906.                 helper(h_folder_open, "HELP FOR OPENING FOLDERS", 0);
  907.                 break;
  908.               case SaveMessage:
  909.                 helper(h_folder_save,"HELP FOR SAVING MESSAGES TO FOLDERS", 0);
  910.                 break;
  911.               case GetFcc:
  912.                 helper(h_folder_fcc, "HELP FOR SELECTING THE FCC", 1);
  913.                 break;
  914.               case Subscribe:
  915.                 helper(h_folder_subscribe,
  916.                 "HELP SELECTING NEWSGROUP TO SUBSCRIBE TO", 1);
  917.                 break;
  918.               case PostNews:
  919.                 helper(h_folder_postnews,
  920.                 "HELP FOR SELECTING NEWSGROUP TO POST TO", 1);
  921.                 break;
  922.             }
  923.             if(ps_global->next_screen != SCREEN_FUN_NULL) {
  924.                 /* So "m" to go back to main menu works */
  925.             return(0);
  926.             }
  927.             ps_global->redrawer = redraw_folder_screen;
  928.         mangled_header++;
  929.         mangled_footer++;
  930.         fs->prev_index = -1;
  931.         if(km_size != FOOTER_ROWS(ps_global))  /* keymenu came or went */
  932.           create_folder_display(fs, ps_global->ttyo->screen_cols);
  933.  
  934.         break;
  935.  
  936.  
  937.             /*---------- Select or View ----------*/
  938.           case ctrl('M') :
  939.           case ctrl('J') :
  940.           case PF4:
  941.           case 'v':
  942.           case 's':
  943.         if((ch == ctrl('M') || ch == ctrl('J'))
  944.            && do_what == Subscribe && doing_listmode)
  945.           goto DoToggle;  /* different default in this case */
  946. DoSelect:
  947.         if((do_what != FolderMaint && ch == 'v') 
  948.            || (do_what == FolderMaint && ch == 's'))
  949.           goto bleep;
  950.         if(!folder_total(fs->context->folders)){
  951.         q_status_message(SM_ORDER | SM_DING, 3, 3, 
  952.                   "Empty folder collection.  Nothing to select!");
  953.         } else if(!ALL_FOUND(fs->context) 
  954.               || (fs->context->use & CNTXT_PSEUDO)){
  955.         if(fs->context->use & CNTXT_PSEUDO){
  956.             folder_delete(0, fs->context->folders);
  957.             fs->context->use &= ~CNTXT_PSEUDO;
  958.         }
  959.  
  960.         if(!folder_total(fs->context->folders))
  961.           fs->context->use |= CNTXT_NOFIND; /* ok to try find */
  962.  
  963.         find_folders_in_context(NULL, fs->context, NULL);
  964.  
  965.         if(fs->context == ps->context_current
  966.            && (fs->folder_index = folder_index(ps->cur_folder, 
  967.                             fs->context->folders)) < 0)
  968.           fs->folder_index = 0;
  969.  
  970.         create_folder_display(fs, ps_global->ttyo->screen_cols);
  971.  
  972.         fs->prev_index = -1;          /* redraw display */
  973.  
  974.         if(do_what == FolderMaint)
  975.           mangled_footer++;
  976.         } else if(do_what == FolderMaint) {
  977.         
  978.                 /*--- Open folder ---*/
  979.         if(cur_name == NULL)
  980.           break;
  981.  
  982.                 if(do_broach_folder(cur_name, fs->context) == 1) {
  983.             for(tc = fs->context_list; tc ; tc = tc->next)
  984.               free_folders_in_context(tc);
  985.  
  986.                     ps_global->redrawer = (void(*)())NULL;
  987.                     ps_global->next_screen = mail_index_screen;
  988.                     return(1); 
  989.                 }
  990.  
  991.                 /* Open Failed. Message will be issued by do_broach_folder. */
  992.                 /* Stay here in folder lister and let the user try again. */
  993.         mangled_footer++;
  994.                 break;
  995.             } else {
  996.                 /*-- save message, subscribe or post --- */
  997.                 if((do_what == GetFcc || do_what == SaveMessage)
  998.            && (fs->context->type & FTYPE_BBOARD)) {
  999.                     q_status_message(SM_ORDER | SM_DING, 3, 4,
  1000.              "Can't save messages to bulletin boards or news groups!");
  1001.                     break;
  1002.                 }
  1003.  
  1004.             if(cur_name == NULL){
  1005.             rv = 0;
  1006.             }
  1007.         else{
  1008.             if(do_what == Subscribe){
  1009.             int i, n=0, folder_n;
  1010.  
  1011.             if(return_context)
  1012.               *return_context = fs->context;
  1013.  
  1014.             folder_n = folder_total(fs->context->folders);
  1015.             /* count X's */
  1016.             if(doing_listmode)
  1017.               for(i=0; i < folder_n; i++)
  1018.                 if(folder_entry(i,
  1019.                     fs->context->folders)->prefix[1] == 'X')
  1020.                   n++;
  1021.  
  1022.             if(n == 0 && !strncmp(folder_entry(fs->folder_index,
  1023.                       fs->context->folders)->prefix,"SUB",3)){
  1024.                 q_status_message1(SM_ORDER,0,4,
  1025.                 "Already subscribed to \"%s\"",
  1026.                 cur_name == NULL ? "" : cur_name);
  1027.                 break;
  1028.             }
  1029.  
  1030.             if(n == 0 && doing_listmode){
  1031.                 q_status_message(SM_ORDER,0,1,
  1032.                 "Use \"X\" to mark groups to subscribe to");
  1033.                 break;
  1034.             }
  1035.  
  1036.             /* if more than one name, return in the array */
  1037.             if(return_array && n > 1){
  1038.                 char question_buf[80];
  1039.                 int ans;
  1040.  
  1041.                 sprintf(question_buf, "Subscribe to %d new groups",
  1042.                     n);
  1043.                 if(F_OFF(F_SELECT_WO_CONFIRM,ps_global)
  1044.                   && (ans=want_to(question_buf,'n','x',NO_HELP,0,0))
  1045.                     != 'y'){
  1046.                 if(ans == 'x')
  1047.                   q_status_message(SM_ORDER,0,1,
  1048.                 "Use \"ExitSubscb\" to cancel subscribe command");
  1049.  
  1050.                 mangled_footer++;
  1051.                 break;
  1052.                 }
  1053.  
  1054.                 *return_array = (char **)fs_get(sizeof(char *)
  1055.                                 * (n+1));
  1056.                 memset((void *)*return_array,0,
  1057.                     sizeof(char *)*(n+1));
  1058.                 for(i=0,n=0; i < folder_n; i++){
  1059.                 if(folder_entry(i,
  1060.                     fs->context->folders)->prefix[1] == 'X'){
  1061.                     (*return_array)[n++]
  1062.                     = cpystr(folder_entry(i,
  1063.                           fs->context->folders)->name);
  1064.                 }
  1065.                 }
  1066.  
  1067.                 rv = 17;
  1068.             }
  1069.             else if(return_string){
  1070.                 strcpy(return_string, cur_name);
  1071.                 rv = 1;
  1072.             }
  1073.             else
  1074.               rv = 0;
  1075.             }
  1076.             else{
  1077.             if(return_context)
  1078.               *return_context = fs->context;
  1079.  
  1080.             if(return_string)
  1081.               strcpy(return_string,
  1082.                  (do_what == GetFcc && cur_f)
  1083.                    ? cur_f->name : cur_name);
  1084.  
  1085.             rv = 1;
  1086.             dprint(5, (debugfile,
  1087.                   "return \"%s\" in context \"%s\"\n",
  1088.                   (return_string) ? return_string : "NULL", 
  1089.                   (return_context) ? (*return_context)->context 
  1090.                             : "NULL"));
  1091.             }
  1092.             }
  1093.  
  1094.                 if(do_what != PostNews)
  1095.           for(tc = fs->context_list; tc ; tc = tc->next)
  1096.             free_folders_in_context(tc);
  1097.  
  1098.                 ps_global->redrawer = (void (*)())NULL;
  1099.             return(rv);
  1100.             }
  1101.  
  1102.         break;
  1103.  
  1104.  
  1105.             /*--------- Hidden "To Fldrs" command -----------*/
  1106.       case 'l':
  1107.       ListModeOn:
  1108.         if(do_what == FolderMaint)
  1109.           q_status_message(SM_ORDER, 0, 3, "Already in Folder List");
  1110.         else if(do_what == Subscribe && !doing_listmode){
  1111.         int i, folder_n;
  1112.  
  1113.         doing_listmode++;
  1114.         folder_n = folder_total(fs->context->folders);
  1115.         for(i=0; i < folder_n; i++){
  1116.             if(strncmp(folder_entry(i,fs->context->folders)->prefix,
  1117.             "SUB", 3) != 0){
  1118.             folder_entry(i, fs->context->folders)->prefix[0] = '[';
  1119.             folder_entry(i, fs->context->folders)->prefix[2] = ']';
  1120.             }
  1121.         }
  1122.  
  1123.         redraw_folder_screen();
  1124.         q_status_message(SM_ORDER,0,1,
  1125.             "Use \"X\" to mark groups to subscribe to");
  1126.         mangled_footer++;
  1127.         }
  1128.         else
  1129.           goto bleep;
  1130.  
  1131.         break;
  1132.  
  1133.     
  1134.             /*--------- EXIT menu -----------*/
  1135.       case PF3:
  1136.       case 'm':
  1137.       case 'e':
  1138.         if((do_what != FolderMaint && ch == 'm') 
  1139.            || (do_what == FolderMaint && ch == 'e'))
  1140.           goto bleep;
  1141.  
  1142.         if(doing_listmode){
  1143.         int i, folder_n;
  1144.  
  1145.         folder_n = folder_total(fs->context->folders);
  1146.  
  1147.         /* any X's? */
  1148.         for(i=0; i < folder_n; i++)
  1149.           if(folder_entry(i, fs->context->folders)->prefix[1] == 'X')
  1150.             break;
  1151.  
  1152.         if(i < folder_n        /* some selections have been made */
  1153.            && want_to("Really abandon your selections ",
  1154.                   'y', 'x', NO_HELP, 0, 0) != 'y'){
  1155.             mangled_footer++;
  1156.             break;
  1157.         }
  1158.         }
  1159.  
  1160.         ps_global->redrawer = (void (*)())NULL;
  1161.         for(tc = fs->context_list; tc ; tc = tc->next)
  1162.           free_folders_in_context(tc);
  1163.  
  1164.         return(0);
  1165.  
  1166.  
  1167.             /*--------- QUIT pine -----------*/
  1168.           case OPF3:
  1169.       case 'q':
  1170.             if(do_what != FolderMaint)
  1171.                 goto bleep;
  1172.  
  1173.         for(tc = fs->context_list; tc ; tc = tc->next)
  1174.           free_folders_in_context(tc);
  1175.  
  1176.             ps_global->redrawer = (void (*)())NULL;
  1177.             ps_global->next_screen = quit_screen;
  1178.         return(0);
  1179.         
  1180.  
  1181.             /*--------- Compose -----------*/
  1182.           case OPF4:
  1183.       case 'c':
  1184.             if(do_what != FolderMaint)
  1185.                 goto bleep;
  1186.  
  1187.         ps_global->redrawer = (void (*)())NULL;
  1188.         for(tc = fs->context_list; tc ; tc = tc->next)
  1189.           free_folders_in_context(tc);
  1190.  
  1191.             ps_global->next_screen = compose_screen;
  1192.         return(0);
  1193.         
  1194.  
  1195.             /*--------- Message Index -----------*/
  1196.       case OPF7:
  1197.       case 'i':
  1198.         if(do_what != FolderMaint)
  1199.           goto bleep;
  1200.  
  1201.         ps_global->redrawer = (void (*)())NULL;
  1202.         for(tc = fs->context_list; tc ; tc = tc->next)
  1203.           free_folders_in_context(tc);
  1204.  
  1205.         ps_global->next_screen = mail_index_screen;
  1206.             q_status_message(SM_ORDER, 0, 2, "Returning to current index");
  1207.         return(0);
  1208.  
  1209.  
  1210.             /*----------------- Add a new folder name -----------*/
  1211.       case PF10:
  1212.       case 'a':
  1213.             /*--------------- Rename folder ----------------*/
  1214.       case PF11:
  1215.       case 'r':
  1216.         if(do_what != FolderMaint)
  1217.           goto bleep;
  1218.  
  1219.         if(ch == 'r' || ch == PF11)
  1220.           new_file =rename_folder(quest_line,fs->folder_index,fs->context);
  1221.         else {
  1222.                 if(fs->context->type & FTYPE_BBOARD)
  1223.                   new_file = group_subscription(quest_line, fs->context);
  1224.                 else
  1225.           new_file = add_new_folder(quest_line, fs->context);
  1226.             }
  1227.  
  1228.             if(new_file && ALL_FOUND(fs->context)) {
  1229.                 /* place cursor on new folder! */
  1230.         create_folder_display(fs, ps->ttyo->screen_cols);
  1231.         fs->prev_index = -1;
  1232.         fs->folder_index = folder_index(new_file, fs->context->folders);
  1233.             }
  1234.  
  1235.         if(fs->prev_index < 0)
  1236.           mangled_header++;
  1237.  
  1238.         mangled_footer++;
  1239.             break;
  1240.              
  1241.  
  1242.             /*-------------- Delete --------------------*/
  1243.           case PF9:
  1244.       case 'd':
  1245.         if(do_what == Subscribe && ch == PF9){
  1246.         if(doing_listmode)
  1247.               goto DoToggle;
  1248.         else
  1249.           goto ListModeOn;
  1250.         }
  1251.  
  1252.         if(do_what != FolderMaint)
  1253.           goto bleep;
  1254.  
  1255.         if(!ALL_FOUND(fs->context) || (fs->context->use & CNTXT_PSEUDO)){
  1256.         q_status_message1(SM_ORDER | SM_DING, 0, 3,
  1257.                   "No folder selected to delete.  %s list.",
  1258.                   ALL_FOUND(fs->context) ? "Empty" : "Expand");
  1259.         break;
  1260.         }
  1261.  
  1262.             if(delete_folder(fs->folder_index, fs->context, &mangled_header)){
  1263.         /* remove from file list */
  1264.                 folder_delete(fs->folder_index, fs->context->folders);
  1265.  
  1266.         if(fs->folder_index >= folder_total(fs->context->folders))
  1267.           fs->folder_index = max(0, fs->folder_index - 1);
  1268.  
  1269.         create_folder_display(fs, ps->ttyo->screen_cols);
  1270.                 fs->prev_index = -1; /* Force redraw */
  1271.             }
  1272.  
  1273.             mangled_footer++;
  1274.         break;
  1275.  
  1276.  
  1277.             /*-------------- Toggle subscribe checkbox --------------------*/
  1278.       case 'x':
  1279. DoToggle:
  1280.         if(!(do_what == Subscribe && doing_listmode))
  1281.           goto bleep;
  1282.  
  1283.         if(!strncmp(folder_entry(fs->folder_index,
  1284.                     fs->context->folders)->prefix, "SUB", 3)){
  1285.         q_status_message1(SM_ORDER,0,4,
  1286.             "Already subscribed to \"%s\"",
  1287.             cur_name == NULL ? "" : cur_name);
  1288.         break;
  1289.         }
  1290.         else{
  1291.         folder_entry(fs->folder_index,fs->context->folders)->prefix[1]
  1292.             = (folder_entry(fs->folder_index,
  1293.                    fs->context->folders)->prefix[1] == 'X')
  1294.                 ? ' '
  1295.                 : 'X';
  1296.         }
  1297.  
  1298.         break;
  1299.  
  1300.  
  1301.              /*------------- Print list of folders ---------*/
  1302.           case OPF9:
  1303.       case 'y':
  1304.         if(do_what != FolderMaint)
  1305.           goto bleep;
  1306.  
  1307.             print_folders(fs);
  1308.         mangled_footer++;
  1309.         break;
  1310.  
  1311.  
  1312.             /*---------- Look (Search) ----------*/
  1313.       case OPF8:
  1314.       case PF12:
  1315.       case 'w':
  1316.       case ctrl('W'):
  1317.         if((do_what != FolderMaint && ch == OPF8) 
  1318.            || (do_what == FolderMaint && ch == PF12))
  1319.           goto bleep;
  1320.             mangled_footer++;
  1321.             rc = search_folders(fs, quest_line);
  1322.             if(rc == -1)
  1323.           q_status_message(SM_ORDER, 0, 2, "Folder name search cancelled");
  1324.         else if(rc == 0)
  1325.           q_status_message(SM_ORDER | SM_DING, 0, 2, "Word not found");
  1326.         else if(rc == 2)
  1327.               q_status_message(SM_ORDER, 0, 2, "Search wrapped to beginning");
  1328.  
  1329.         break;
  1330.  
  1331.  
  1332.             /*---------- Go to Folder, ----------*/
  1333.           case OPF6:
  1334.           case 'g':
  1335.         if(do_what != FolderMaint)
  1336.           goto bleep;
  1337.  
  1338.             new_fold = broach_folder(-FOOTER_ROWS(ps), 0, &(fs->context));
  1339.             mangled_footer = 1;;
  1340.             if(new_fold && do_broach_folder(new_fold, fs->context) > 0){
  1341.         for(tc = fs->context_list; tc ; tc = tc->next)
  1342.           free_folders_in_context(tc);
  1343.  
  1344.         ps_global->redrawer = (void (*)())NULL;
  1345.         ps_global->next_screen = mail_index_screen;
  1346.         return(1); 
  1347.         }
  1348.  
  1349.             break;
  1350.  
  1351. #ifdef NEWBB
  1352.             /*------------ Dismiss the recently created news groups -----*/
  1353.           case 'x':
  1354.             if(do_what != Subscribe)
  1355.               goto bleep;
  1356.             clear_new_groups(fs->context->folders);
  1357.             redraw_folder_screen();
  1358.             q_status_message(SM_ORDER, 0, 2,
  1359.          "Mark on newly created groups cleared; groups sorted into full list");
  1360.             break;
  1361. #endif
  1362.  
  1363.             /*----------no op to check for new mail -------*/
  1364.           case NO_OP_IDLE:
  1365.       case NO_OP_COMMAND :
  1366.         break;
  1367.  
  1368.  
  1369.             /*------------ Redraw command -------------*/
  1370.           case KEY_RESIZE:
  1371.           case ctrl('L'):
  1372.             ClearScreen();
  1373.             mangled_footer++;
  1374.             mangled_header++;
  1375.             redraw_folder_screen();
  1376.         break;
  1377.  
  1378.  
  1379.             /*--------------- Invalid Command --------------*/
  1380.       default: 
  1381.           bleep:
  1382.         bogus_command(orig_ch, F_ON(F_USE_FK,ps) ? "F1" : "?");
  1383.         break;
  1384.     } /* End of switch */
  1385.     }
  1386. }
  1387.  
  1388.  
  1389.  
  1390. /*----------------------------------------------------------------------
  1391.      Adjust the folder list's display down one line
  1392.  
  1393. Uses the static fs data structure so it can be called almost from
  1394. any context. It will recalculate the screen size if need be.
  1395.   ----*/
  1396. int
  1397. folder_scroll_down(count)
  1398.     long count;
  1399. {
  1400.     if(count < 0)
  1401.     return(folder_scroll_up(-count));
  1402.     else if(count){
  1403.     while(count-- && fs->top_row)
  1404.       fs->top_row--;
  1405.  
  1406.     if(folder_entry(fs->folder_index, fs->context->folders)->d_line
  1407.                 >= (unsigned)(fs->top_row + fs->display_rows)){
  1408.         for(count = fs->top_row + fs->display_rows - 1;
  1409.         count >= 0 && folder_list_entry(fs, (int)count,
  1410.                 &(fs->folder_index), &(fs->context)) && count >= 0;
  1411.         count--)
  1412.           ; 
  1413.  
  1414.         fs->prev_index   = fs->folder_index;
  1415.         fs->prev_context = fs->context;
  1416.     }
  1417.  
  1418.     }
  1419.  
  1420.     return(1);
  1421. }
  1422.  
  1423.  
  1424.  
  1425. /*----------------------------------------------------------------------
  1426.      Adjust the folder list's display up one line
  1427.  
  1428. Uses the static fs data structure so it can be called almost from
  1429. any context. It will recalculate the screen size if need be.
  1430.   ----*/
  1431. int
  1432. folder_scroll_up(count)
  1433.     long count;
  1434. {
  1435.     if(count < 0)
  1436.       return(folder_scroll_down(-count));
  1437.     else if(count){
  1438.     while(count-- && fs->top_row < fs->last_row)
  1439.       fs->top_row++;
  1440.  
  1441.     if(folder_entry(fs->folder_index,
  1442.             fs->context->folders)->d_line < (unsigned)fs->top_row){
  1443.         for(count = fs->top_row;
  1444.         folder_list_entry(fs, (int)count, &(fs->folder_index),
  1445.                   &(fs->context))
  1446.         && count <= folder_total(fs->context->folders);
  1447.         count++)
  1448.           ;
  1449.  
  1450.         fs->prev_index   = fs->folder_index;
  1451.         fs->prev_context = fs->context;
  1452.     }
  1453.     }
  1454.  
  1455.     return(1);
  1456. }
  1457.  
  1458.  
  1459.  
  1460. /*----------------------------------------------------------------------
  1461.      Adjust the folder list's display so the given line starts the page
  1462.  
  1463. Uses the static fs data structure so it can be called almost from
  1464. any context. It will recalculate the screen size if need be.
  1465.   ----*/
  1466. int
  1467. folder_scroll_to_pos(line)
  1468.     long line;
  1469. {
  1470.     return(folder_scroll_up(line - fs->top_row));
  1471. }
  1472.  
  1473.  
  1474.  
  1475. /*----------------------------------------------------------------------
  1476.      Redraw the folders screen
  1477.  
  1478. Uses the static fs data structure so it can be called almost from
  1479. any context. It will recalculate the screen size if need be.
  1480.   ----*/
  1481. void
  1482. redraw_folder_screen()
  1483. {
  1484.     create_folder_display(fs, ps_global->ttyo->screen_cols);
  1485.     display_folder(fs, fs->context, fs->folder_index, NULL, -1);
  1486. }
  1487.  
  1488.                 
  1489.  
  1490. /*----------------------------------------------------------------------
  1491.    Arrange and paint the lines of the folders directory on the screen
  1492.  
  1493.    Args: fd      -- The folder display structure
  1494.          lines   -- The number of folder lines to display on the screen
  1495.   
  1496.  Result: the lines are painted or repainted on the screen
  1497.  
  1498.     Paint folder list, or part of it.
  1499.  
  1500. Called to either paint or repaint the whole list, or just move the
  1501. cursor. If old_row is -1 then we are repainting. In this case have
  1502. to watch out for names that are blank as the data sometimes has blanks in
  1503. it. Go through the data row by row, column by column. Paint the item that's 
  1504. currently "it" in reverse.
  1505.  
  1506. When the changing the current one, just repaint the old one normal and the
  1507. new one reverse.
  1508.  
  1509.   ----*/
  1510. void
  1511. display_folder(fd, cntxt, index, old_cntxt, old_index)
  1512.      FSTATE_S  *fd;
  1513.      CONTEXT_S *cntxt, *old_cntxt;
  1514.      int        index, old_index;
  1515. {
  1516.     CONTEXT_S *c;
  1517.     char      *s;
  1518.     int        i, row;
  1519.  
  1520.     if(fd->display_rows <= 0)        /* room for display? */
  1521.       return;                /* nope. */
  1522.  
  1523. #ifdef _WINDOWS
  1524.     mswin_beginupdate();
  1525. #endif
  1526.     /*
  1527.      * Check the framing of the current context/folder...
  1528.      */
  1529.     if(old_index < 0 || off_folder_display(fd, index, cntxt)){
  1530.     /*------------ check framing, then... -----------*/
  1531.     while(off_folder_display(fd, index, cntxt) < 0)
  1532.       fd->top_row = max(0, fd->top_row - fd->display_rows);
  1533.  
  1534.     while(off_folder_display(fd, index, cntxt) > 0)
  1535.       fd->top_row = min(fd->last_row, fd->top_row + fd->display_rows);
  1536.  
  1537.     /*------------ repaint entire screen _-----------*/
  1538.     for(row = 0; row < fd->display_rows; row++){
  1539.         ClearLine(HEADER_ROWS(ps_global) + row);
  1540.  
  1541.         if(s = folder_list_entry(fd, row + fd->top_row, &i, &c)){
  1542.         /*-------- paint text centered --------*/
  1543.         if(*s){
  1544.             PutLine0(HEADER_ROWS(ps_global) + row,
  1545.                  max(0, (fd->display_cols/2)-(strlen(s)/2)),
  1546.                  s);
  1547.         }
  1548.         }
  1549.         else{            /* paint folder names on row */
  1550.         do
  1551.           paint_folder_name((i == index && c == cntxt), fd, i, c);
  1552.         while(++i < folder_total(c->folders)
  1553.               && folder_entry(i,c->folders)->d_line==row+fd->top_row
  1554.               && !(c->use & CNTXT_PSEUDO));
  1555.         }
  1556.     }
  1557.     }
  1558.     else{                /* restore old name to normal */
  1559.     if(folder_total(old_cntxt->folders) 
  1560.        && !off_folder_display(fd, old_index, old_cntxt))
  1561.       paint_folder_name(0, fd, old_index, old_cntxt);
  1562.  
  1563.     if(folder_total(cntxt->folders))
  1564.       paint_folder_name(1, fd, index, cntxt); /* and hilite the new name */
  1565.     }
  1566. #ifdef _WINDOWS
  1567.     scroll_setrange(fd->last_row);
  1568.     scroll_setpos(fd->top_row);
  1569.     mswin_endupdate();
  1570. #endif
  1571.     fflush(stdout);
  1572. }
  1573.  
  1574.  
  1575.  
  1576. /*
  1577.  * paint_folder_name - paint the folder at the given index in the given
  1578.  *                     collection.  
  1579.  */
  1580. void
  1581. paint_folder_name(hilite, fd, index, context)
  1582.     int        hilite, index;
  1583.     FSTATE_S  *fd;
  1584.     CONTEXT_S *context;
  1585. {
  1586.     FOLDER_S *f;
  1587.     char     *s;
  1588.  
  1589.     if((f = folder_entry(index, context->folders)) == NULL || f->name == NULL)
  1590.       return;
  1591.  
  1592.     if(context->type&FTYPE_BBOARD || context->use&CNTXT_INCMNG)
  1593.       sprintf(s = tmp_20k_buf, "%s%s", f->prefix, FLDR_NAME(f));
  1594.     else
  1595.       s = FLDR_NAME(f);
  1596.  
  1597.     MoveCursor(HEADER_ROWS(ps_global) + (f->d_line - fd->top_row), f->d_col);
  1598.     if(hilite)
  1599.       StartInverse();
  1600.  
  1601.     Write_to_screen(s);
  1602.     if(hilite)
  1603.       EndInverse();
  1604. }
  1605.  
  1606.  
  1607. #if (defined(DOS) && !defined(_WINDOWS)) || defined(OS2)
  1608. #define LINECH    '\xC4'
  1609. #else
  1610. #define LINECH    '-'
  1611. #endif
  1612.  
  1613. /*
  1614.  * folder_list_entry - return either the string associated with the
  1615.  *                     current folder list line, or the first
  1616.  *                     folder index and context associated with it.
  1617.  *
  1618.  * NOTE: this is kind of dumb right now since it starts from the top 
  1619.  *       each time it's called.  Caching the last values would make it
  1620.  *       alot less costly.
  1621.  */
  1622. char *
  1623. folder_list_entry(fd, goal_row, index, context)
  1624.     FSTATE_S   *fd;
  1625.     int         goal_row;
  1626.     int        *index;
  1627.     CONTEXT_S **context;
  1628. {
  1629.     char     *s;
  1630.     int       row, label, ftotal;
  1631.  
  1632.     if(goal_row > fd->last_row)     /* return blanks past last entry */
  1633.       return("");
  1634.  
  1635.     goal_row = max(0, goal_row);
  1636.     row      = 0;
  1637.     *index   = 0;
  1638.     for(*context = fs->context_list;
  1639.     (*context)->next && (*context)->next->d_line < goal_row;
  1640.     *context = (*context)->next)
  1641.       row = (*context)->next->d_line;
  1642.  
  1643.     s        = NULL;
  1644.     label    = (ps_global->context_list->next) ? 1 : 0;
  1645.  
  1646.     while(1){
  1647.     if(label > 0){
  1648.         if(row < (*context)->d_line){
  1649.         memset((void *)tmp_20k_buf,LINECH,fd->display_cols*sizeof(char));
  1650.         tmp_20k_buf[fd->display_cols] = '\0';
  1651.         s = (row + 1 == (*context)->d_line) ? tmp_20k_buf : "" ;
  1652.         }
  1653.         else if(label++ < 2){
  1654.         s = tmp_20k_buf;
  1655.         if((*context)->use & CNTXT_INCMNG){
  1656.             sprintf(tmp_20k_buf, "%s", (*context)->label[0]);
  1657.         }
  1658.         else{
  1659.             memset((void *)tmp_20k_buf, ' ', 
  1660.                fd->display_cols * sizeof(char));
  1661.             tmp_20k_buf[fd->display_cols] = '\0';
  1662.  
  1663.             sprintf(tmp_20k_buf + fd->display_cols + 2, 
  1664.                 "%s-collection <%s>  %s", 
  1665.                 ((*context)->type & FTYPE_BBOARD) ? "News"
  1666.                                       : "Folder",
  1667.                 (*context)->label[0], 
  1668.                 ((*context)->use & CNTXT_SAVEDFLT)
  1669.                     ? "** Default for Saves **" : "");
  1670.             strncpy(tmp_20k_buf, 
  1671.                 tmp_20k_buf + fd->display_cols + 2, 
  1672.                 strlen(tmp_20k_buf + fd->display_cols + 2));
  1673.             strncpy(tmp_20k_buf + fd->display_cols 
  1674.                 - (((*context)->type & FTYPE_REMOTE) ? 8 : 7),
  1675.                 ((*context)->type & FTYPE_REMOTE)? "(Remote)"
  1676.                                                  : "(Local)",
  1677.                 ((*context)->type & FTYPE_REMOTE) ? 8 : 7);
  1678.         }
  1679.         }
  1680.         else{
  1681.         label = -1;
  1682.         continue;
  1683.         }
  1684.     }
  1685.     else if((ftotal = folder_total((*context)->folders))
  1686.         && row < (int)folder_entry(0, (*context)->folders)->d_line){
  1687.         if(label < 0){
  1688.         memset((void *)tmp_20k_buf,LINECH,fd->display_cols*sizeof(char));
  1689.         tmp_20k_buf[fd->display_cols] = '\0';
  1690.         s = tmp_20k_buf;
  1691.         label = 0;
  1692.         }
  1693.         else
  1694.           s = "";
  1695.     }
  1696.     else{
  1697.         s          = NULL;
  1698.         if(*index >= ftotal        /* maybe continue where we left off? */
  1699.            || (int)folder_entry(*index,
  1700.                     (*context)->folders)->d_line > goal_row)
  1701.           *index = 0;
  1702.  
  1703.         for(; *index < ftotal; (*index)++)
  1704.           if(goal_row <= (int)folder_entry(*index,
  1705.                            (*context)->folders)->d_line)
  1706.         break;            /* bingo! */
  1707.  
  1708.         if(*index >= ftotal){
  1709.         *index = 0;
  1710.         if((*context)->next){
  1711.             *context = (*context)->next;
  1712.             label      = 1;
  1713.             continue;        /* go take care of labels */
  1714.         }
  1715.         else{
  1716.             s = "";            /* dropped off end of list */
  1717.             break;
  1718.         }
  1719.         }
  1720.     }
  1721.  
  1722.     if(row == goal_row)
  1723.       break;
  1724.     else
  1725.       row++;
  1726.     }
  1727.  
  1728.     *index = max(0, *index);
  1729.     return(s);
  1730. }
  1731.  
  1732.  
  1733.  
  1734. /*
  1735.  * off_folder_display - returns: 0 if given folder is on display,
  1736.  *                               1 if given folder below display, and
  1737.  *                              -1 if given folder above display
  1738.  */
  1739. int
  1740. off_folder_display(fd, index, context)
  1741.     FSTATE_S  *fd;
  1742.     int        index;
  1743.     CONTEXT_S *context;
  1744. {
  1745.     int l;
  1746.  
  1747.     if(index >= folder_total(context->folders))
  1748.       return(0);            /* no folder to display */
  1749.  
  1750.     if((l = folder_entry(index, context->folders)->d_line) < fd->top_row)
  1751.       return(-1);
  1752.     else if(l >= (fd->top_row + fd->display_rows))
  1753.       return(1);
  1754.     else
  1755.       return(0);
  1756. }
  1757.  
  1758.  
  1759.  
  1760. /*----------------------------------------------------------------------
  1761.       Create a new folder
  1762.  
  1763.    Args: quest_line  -- Screen line to prompt on
  1764.          folder_list -- The current list of folders
  1765.  
  1766.  Result: returns the name of the folder created
  1767.  
  1768.   ----*/
  1769.  
  1770. char *
  1771. add_new_folder(quest_line, cntxt)
  1772.      int        quest_line;
  1773.      CONTEXT_S *cntxt;
  1774. {
  1775.     static char  add_folder[MAXFOLDER+1];    /* needed after return!! */
  1776.     char     tmp[MAXFOLDER+1], nickname[32], c, *return_val = NULL;
  1777.     HelpType     help;
  1778.     int          rc, offset, exists, cnt = 0;
  1779.     MAILSTREAM  *create_stream;
  1780.     FOLDER_S    *f;
  1781.  
  1782.     dprint(4, (debugfile, "\n - add_new_folder - \n"));
  1783.     
  1784.     add_folder[0] = '\0';
  1785.     nickname[0]   = '\0';
  1786.     if(cntxt->use & CNTXT_INCMNG){
  1787.     char inbox_host[MAXPATH], *beg, *end = NULL;
  1788.     ESCKEY_S *special_key;
  1789.     static ESCKEY_S host_key[] = {{ctrl('X'),12,"^X","Use Inbox Host"},
  1790.                       {-1, 0, NULL, NULL}};
  1791.  
  1792.     if(ps_global->readonly_pinerc){
  1793.         q_status_message(SM_ORDER,3,5,
  1794.         "Addition cancelled: config file not editable");
  1795.         return(NULL);
  1796.     }
  1797.  
  1798.     /*
  1799.      * Prompt for the full pathname (with possible "news" subcommand),
  1800.      * then fall thru to prompt for foldername, then prompt for 
  1801.      * nick name.
  1802.      * NOTE : Don't put it in a "[<default]" prompt since we 
  1803.      * need a way to chose a local folder!
  1804.      */
  1805.     inbox_host[0] = '\0';
  1806.     if((beg = ps_global->VAR_INBOX_PATH)
  1807.        && (*beg == '{' || (*beg == '*' && *++beg == '{'))
  1808.        && (end = strindex(ps_global->VAR_INBOX_PATH, '}'))){
  1809.         strncpy(inbox_host, beg+1, end - beg);
  1810.         inbox_host[end - beg - 1] = '\0';
  1811.         special_key = host_key;
  1812.     }
  1813.     else
  1814.       special_key = NULL;
  1815.  
  1816.     sprintf(tmp, "Name of server to contain added folder : ");
  1817.     help = NO_HELP;
  1818.     while(1){
  1819.         rc = optionally_enter(add_folder, quest_line, 0, MAXFOLDER, 1,
  1820.                           0, tmp, special_key, help, 0);
  1821.         removing_trailing_white_space(add_folder);
  1822.         removing_leading_white_space(add_folder);
  1823.         if(rc == 3){
  1824.         help = help == NO_HELP ? h_incoming_add_folder_host : NO_HELP;
  1825.         }
  1826.         else if(rc == 12){
  1827.         strcpy(add_folder, inbox_host);
  1828.         break;
  1829.         }
  1830.         else if(rc == 1){
  1831.         q_status_message(SM_ORDER,0,2,
  1832.             "Addition of new folder cancelled");
  1833.         return(NULL);
  1834.         }
  1835.         else if(rc == 0)
  1836.           break;
  1837.     }
  1838.     }
  1839.  
  1840.     if(offset = strlen(add_folder)){        /* must be host for incoming */
  1841.     int i;
  1842.     sprintf(tmp, "Folder on \"%s\" to add : ", add_folder);
  1843.     for(i = offset;i >= 0; i--)
  1844.       add_folder[i+1] = add_folder[i];
  1845.  
  1846.     add_folder[0] = '{';
  1847.     add_folder[++offset] = '}';
  1848.     add_folder[++offset] = '\0';        /* +2, total */
  1849.     }
  1850.     else
  1851.       sprintf(tmp, "Name of folder to add : ");
  1852.  
  1853.     help = NO_HELP;
  1854.     while(1){
  1855.         rc = optionally_enter(&add_folder[offset], quest_line, 0, 
  1856.                   MAXFOLDER - offset, 1, 0, tmp, NULL, help, 0);
  1857.     removing_trailing_white_space(&add_folder[offset]);
  1858.     removing_leading_white_space(&add_folder[offset]);
  1859.         if(rc == 0 && add_folder[offset]){
  1860.         if(!ps_global->show_dot_names && add_folder[offset] == '.'){
  1861.         if(cnt++ <= 0)
  1862.                   q_status_message(SM_ORDER,3,3,
  1863.             "Folder name can't begin with dot");
  1864.         else{
  1865.             NAMEVAL_S *feat;
  1866.             int i;
  1867.  
  1868.             for(i=0; (feat=feature_list(i))
  1869.                 && (feat->value != F_ENABLE_DOT_FOLDERS); i++)
  1870.               ;/* do nothing */
  1871.  
  1872.             q_status_message1(SM_ORDER,3,3,
  1873.               "Config feature \"%s\" enables names beginning with dot",
  1874.               feat && feat->name ? feat->name : "");
  1875.         }
  1876.  
  1877.                 display_message(NO_OP_COMMAND);
  1878.                 continue;
  1879.         }
  1880.  
  1881.         if(strucmp(ps_global->inbox_name, nickname))
  1882.           break;
  1883.         else
  1884.           Writechar(BELL, 0);
  1885.     }
  1886.  
  1887.         if(rc == 3){
  1888.         help = (help == NO_HELP)
  1889.             ? ((cntxt->use & CNTXT_INCMNG)
  1890.                 ? h_incoming_add_folder_name
  1891.                 : h_oe_foldadd)
  1892.             : NO_HELP;
  1893.     }
  1894.     else if(rc == 1 || add_folder[0] == '\0') {
  1895.         q_status_message(SM_ORDER,0,2, "Addition of new folder cancelled");
  1896.         return(NULL);
  1897.     }
  1898.     }
  1899.  
  1900.     if(*add_folder == '{'            /* remote? */
  1901.        || (*add_folder == '*' && *(add_folder+1) == '{')){
  1902.     create_stream = context_same_stream(cntxt->context, add_folder,
  1903.                         ps_global->mail_stream);
  1904.         if(!create_stream 
  1905.            && ps_global->mail_stream != ps_global->inbox_stream)
  1906.           create_stream = context_same_stream(cntxt->context, add_folder,
  1907.                           ps_global->inbox_stream);
  1908.     }
  1909.     else
  1910.       create_stream = NULL;
  1911.  
  1912.     help = NO_HELP;
  1913.     if(cntxt->use & CNTXT_INCMNG){
  1914.     sprintf(tmp, "Nickname for folder \"%s\" : ", &add_folder[offset]);
  1915.     while(1){
  1916.         rc = optionally_enter(nickname, quest_line, 0, 31, 1, 0, tmp,
  1917.                   NULL, help, 0);
  1918.         removing_leading_white_space(nickname);
  1919.         removing_trailing_white_space(nickname);
  1920.         if(rc == 0){
  1921.         if(strucmp(ps_global->inbox_name, nickname))
  1922.           break;
  1923.         else
  1924.           Writechar(BELL, 0);
  1925.         }
  1926.  
  1927.         if(rc == 3){
  1928.         help = help == NO_HELP
  1929.             ? h_incoming_add_folder_nickname : NO_HELP;
  1930.         }
  1931.         else if(rc == 1 || (rc != 3 && !*nickname)){
  1932.         q_status_message(SM_ORDER,0,2,
  1933.             "Addition of new folder cancelled");
  1934.         return(NULL);
  1935.         }
  1936.     }
  1937.  
  1938.     /*
  1939.      * Already exist?  First, make sure this name won't collide with
  1940.      * anything else in the list.  Next, quickly test to see if it
  1941.      * the actual mailbox exists so we know any errors from 
  1942.      * context_create() are really bad...
  1943.      */
  1944.     for(offset = 0; offset < folder_total(cntxt->folders); offset++){
  1945.         f = folder_entry(offset, cntxt->folders);
  1946.         if(!strucmp(FLDR_NAME(f), nickname[0] ? nickname : add_folder)){
  1947.         q_status_message1(SM_ORDER | SM_DING, 0, 3,
  1948.                   "Incoming folder \"%s\" already exists",
  1949.                   nickname[0] ? nickname : add_folder);
  1950.         return(NULL);
  1951.         }
  1952.     }
  1953.  
  1954.     exists = folder_exists(cntxt->context, add_folder);
  1955.     }
  1956.     else
  1957.       exists = 0;
  1958.  
  1959.     if(exists < 0
  1960.        || (!exists && !context_create(cntxt->context,create_stream,add_folder)
  1961.        && !((cntxt->use & CNTXT_INCMNG) && !context_isambig(add_folder))))
  1962.       return(NULL);        /* c-client should've reported error */
  1963.  
  1964.     if(cntxt->use & CNTXT_INCMNG){
  1965.     f = new_folder(add_folder);
  1966.     if(nickname[0]){
  1967.         f->nickname = cpystr(nickname);
  1968.         f->name_len = strlen(f->nickname);
  1969.     }
  1970.  
  1971.     folder_insert(folder_total(cntxt->folders), f, cntxt->folders);
  1972.     if(!ps_global->USR_INCOMING_FOLDERS){
  1973.         offset = 0;
  1974.         ps_global->USR_INCOMING_FOLDERS =
  1975.                          (char **)fs_get(2*sizeof(char *));
  1976.     }
  1977.     else{
  1978.         for(offset=0;  ps_global->USR_INCOMING_FOLDERS[offset]; offset++)
  1979.           ;
  1980.  
  1981.         fs_resize((void **)&(ps_global->USR_INCOMING_FOLDERS),
  1982.               (offset + 2) * sizeof(char *));
  1983.     }
  1984.  
  1985.     sprintf(tmp, "%s%s%s%s%s", nickname[0] ? "\"" : "",
  1986.         nickname[0] ? nickname : "", nickname[0] ? "\"" : "",
  1987.         nickname[0] ? " " : "", add_folder);
  1988.     ps_global->USR_INCOMING_FOLDERS[offset]   = cpystr(tmp);
  1989.     ps_global->USR_INCOMING_FOLDERS[offset+1] = NULL;
  1990.     write_pinerc(ps_global);        /* save new element */
  1991.  
  1992.     if(nickname[0])
  1993.       strcpy(add_folder, nickname);        /* known by new name */
  1994.  
  1995.     q_status_message1(SM_ORDER, 0, 3, "Folder \"%s\" created",add_folder);
  1996.     return_val = add_folder;
  1997.     }
  1998.     else if(context_isambig(add_folder)){
  1999.     if(ALL_FOUND(cntxt)){
  2000.         if(cntxt->use & CNTXT_PSEUDO){
  2001.         folder_delete(0, cntxt->folders);
  2002.         cntxt->use &= ~CNTXT_PSEUDO;
  2003.         }
  2004.  
  2005.         folder_insert(-1, new_folder(add_folder), cntxt->folders);
  2006.         q_status_message1(SM_ORDER,0,3, "Folder \"%s\" created",add_folder);
  2007.     }
  2008.  
  2009.     return_val = add_folder;
  2010.     }
  2011.     else
  2012.       q_status_message1(SM_ORDER, 0, 3,
  2013.             "Folder \"%s\" created outside current collection",
  2014.             add_folder);
  2015.  
  2016.     return(return_val);
  2017. }
  2018.  
  2019. /*---
  2020.   subscribe context referenced here to mark appropriate entries as new
  2021.   newbb_context referenced in imap.c to know to call mark_folder_as_news
  2022.   ---*/
  2023. static    CONTEXT_S    subscribe_cntxt;
  2024. #ifdef NEWBB
  2025. static    CONTEXT_S    newbb_cntxt;
  2026. #endif
  2027.  
  2028. #ifdef NEWBB
  2029. /*----------------------------------------------------------------------
  2030.       Mark the named folder as "NEW" in subscribed_cntxt
  2031.  
  2032. Args: groups -- the name of the news group that is new
  2033.  
  2034. This is going to be inefficient on a DOS machine where folder_entry() is
  2035. expensive. 
  2036.  ----*/
  2037. void
  2038. mark_folder_as_new(group)
  2039.      char *group;
  2040. {
  2041.     int i;
  2042.  
  2043.     for(i = 0 ; i < folder_total(subscribe_cntxt.folders); i++) {
  2044.         if(strucmp(folder_entry(i, subscribe_cntxt.folders)->name,
  2045.                    group) == 0) {
  2046.             strcpy(folder_entry(i, subscribe_cntxt.folders)->prefix, "NEW ");
  2047.             break;
  2048.         }
  2049.     }
  2050. }
  2051. #endif    
  2052.  
  2053.  
  2054. /*----------------------------------------------------------------------
  2055.     Subscribe to a news group
  2056.  
  2057.    Args: quest_line  -- Screen line to prompt on
  2058.          cntxt       -- The context the subscription is for
  2059.  
  2060.  Result: returns the name of the folder subscribed too
  2061.  
  2062.  
  2063. This builds a complete context for the entire list of possible news groups. 
  2064. It also build a context to find the newly created news groups as 
  2065. determined by data kept in .pinerc.  When the find of these new groups is
  2066. done the subscribed context is searched and the items marked as new. 
  2067. A list of new board is never actually created.
  2068.  
  2069.   ----*/
  2070. char *
  2071. group_subscription(quest_line, cntxt)
  2072.      int        quest_line;
  2073.      CONTEXT_S *cntxt;
  2074. {
  2075.     char       *add_folder, *full_name;
  2076.     char      **folders;
  2077.     int            rc, i, last_find_partial = 0, we_cancel = 0;
  2078.     long        xsum;
  2079.     FOLDER_S       *new_f;
  2080.     MAILSTREAM       *create_stream;
  2081.     FSTATE_S        sub_state, *push_state;
  2082.     HelpType        help;
  2083.     static char        folder[MAXFOLDER+2];
  2084.     static ESCKEY_S subscribe_keys[] = {{ctrl('T'), 12, "^T", "To All Grps"},
  2085.                     {-1, 0, NULL, NULL}};
  2086.     extern long        line_hash();
  2087.  
  2088.     /*---- Build a context to find all news groups -----*/
  2089.     subscribe_cntxt         = *cntxt;
  2090.     subscribe_cntxt.use    |= CNTXT_FINDALL | CNTXT_NOFIND;
  2091.     subscribe_cntxt.use    &= ~CNTXT_PSEUDO;
  2092.     subscribe_cntxt.next    = NULL;
  2093.     subscribe_cntxt.folders = new_folder_list();
  2094.  
  2095.     /*
  2096.      * Prompt for group name.
  2097.      */
  2098.     add_folder  = &folder[1];            /* save position 0 */
  2099.     *add_folder = '\0';
  2100.     help = NO_HELP;
  2101.     while(1){
  2102.     xsum = line_hash(add_folder);
  2103.         rc = optionally_enter(add_folder, quest_line, 0, MAXFOLDER, 1, 0,
  2104.                   SUBSCRIBE_PMT, subscribe_keys, help, 0);
  2105.     removing_trailing_white_space(add_folder);
  2106.     removing_leading_white_space(add_folder);
  2107.         if((rc == 0 && *add_folder) || rc == 12){
  2108.         we_cancel = busy_alarm(1, "Fetching newsgroup list", NULL, 0);
  2109.  
  2110.         if(last_find_partial){
  2111.         /* clean up any previous find results */
  2112.         free_folders_in_context(&subscribe_cntxt);
  2113.         last_find_partial = 0;
  2114.         }
  2115.  
  2116.         if(rc == 12){            /* list the whole enchilada */
  2117.         find_folders_in_context(NULL, &subscribe_cntxt, NULL);
  2118.         }
  2119.         else if(i = strlen(add_folder)){
  2120.         /* clean up after any previous find */
  2121.         folder[0]    = '*';        /* insert preceding '*' */
  2122.         add_folder[i]   = '*';        /* and append '*' */
  2123.         add_folder[i+1] = '\0';
  2124.         find_folders_in_context(NULL, &subscribe_cntxt, folder);
  2125.         add_folder[i] = '\0';
  2126.         }
  2127.         else{
  2128.         q_status_message(SM_ORDER, 0, 2,
  2129.            "No group substring to match! Use ^T to list all news groups.");
  2130.         continue;
  2131.         }
  2132.  
  2133.         /*
  2134.          * If we did a partial find on matches, then we faked a full
  2135.          * find which will cause this to just return.
  2136.          */
  2137.         if(i = folder_total(subscribe_cntxt.folders)){
  2138.         char *f;
  2139.  
  2140.         /*
  2141.          * fake that we've found everything there is to find...
  2142.          */
  2143.         subscribe_cntxt.use &= ~(CNTXT_NOFIND|CNTXT_PARTFIND);
  2144.         last_find_partial = 1;
  2145.  
  2146.         if(i == 1){
  2147.             f = folder_entry(0, subscribe_cntxt.folders)->name;
  2148.             if(!strcmp(f, add_folder)){
  2149.             rc = 1;            /* success! */
  2150.             break;
  2151.             }
  2152.             else{            /* else complete the group */
  2153.             strcpy(add_folder, f);
  2154.             continue;
  2155.             }
  2156.         }
  2157.         else if(xsum == line_hash(add_folder)){
  2158.             /*
  2159.              * See if there wasn't an exact match in the lot.
  2160.              */
  2161.             while(i-- > 0){
  2162.             f = folder_entry(i,subscribe_cntxt.folders)->name;
  2163.             if(!strcmp(f, add_folder))
  2164.               break;
  2165.             else
  2166.               f = NULL;
  2167.             }
  2168.  
  2169.             /* if so, then the user picked it from the list the
  2170.              * last time and didn't change it at the prompt.
  2171.              * Must mean they're accepting it...
  2172.              */
  2173.             if(f){
  2174.             rc = 1;            /* success! */
  2175.             break;
  2176.             }
  2177.         }
  2178.         }
  2179.         else{
  2180.         if(rc == 12)
  2181.           q_status_message(SM_ORDER | SM_DING, 3, 3,
  2182.                    "No groups to select from!");
  2183.         else
  2184.           q_status_message1(SM_ORDER, 3, 3,
  2185.               "News group \"%s\" didn't match any existing groups",
  2186.               add_folder);
  2187.  
  2188.         continue;
  2189.         }
  2190.  
  2191. #ifdef NEWBB
  2192.         /*----- build a context to find new news groups -------*/
  2193.         newbb_cntxt = subscribe_cntxt;
  2194.         newbb_cntxt.use |= CNTXT_NOFIND | CNTXT_NEWBB;
  2195.         newbb_cntxt.use &= ~CNTXT_PSEUDO;
  2196.         newbb_cntxt.next = NULL;
  2197.         newbb_cntxt.folders = new_folder_list(); /* Not realy used */
  2198.         newbb_folder_list = newbb_cntxt.folders;
  2199.         find_folders_in_context(NULL, &newbb_cntxt, NULL);
  2200. #endif
  2201.         /*----- Mark groups that are currently subscribed too ------*/
  2202.         /* but first make sure they're found */
  2203.         find_folders_in_context(NULL, cntxt, NULL);
  2204.         for(i = 0 ; i < folder_total(subscribe_cntxt.folders); i++) {
  2205.         dprint(9, (debugfile, "PREFIX: \"%s\", %s\n",
  2206.                folder_entry(i,subscribe_cntxt.folders)->prefix,
  2207.                folder_entry(i,subscribe_cntxt.folders)->name));
  2208.         if(search_folder_list(cntxt->folders,
  2209.                    folder_entry(i,subscribe_cntxt.folders)->name))
  2210.           strcpy(folder_entry(i, subscribe_cntxt.folders)->prefix,
  2211.              "SUB ");
  2212.         else
  2213.           if(strlen(folder_entry(i,
  2214.                      subscribe_cntxt.folders)->prefix)!= 4)
  2215.             strcpy(folder_entry(i,subscribe_cntxt.folders)->prefix,
  2216.                "    ");
  2217.         }
  2218. #ifdef NEWBB
  2219.         /*-- Get the newly created groups to the top of the list --*/
  2220.         sort_folder_list(subscribe_cntxt.folders, compare_folders_new);
  2221. #endif
  2222.  
  2223.         if(we_cancel)
  2224.           cancel_busy_alarm(-1);
  2225.  
  2226.         /*----- Call the folder lister to do all the work -----*/
  2227.         push_state = fs;
  2228.         folders = NULL;
  2229.         rc = folder_lister(ps_global, Subscribe, &subscribe_cntxt,
  2230.                    NULL, add_folder, &folders, &subscribe_cntxt,
  2231.                    &sub_state);
  2232.         fs = push_state;
  2233.         redraw_folder_screen();
  2234.  
  2235.         if(rc <= 0){
  2236.         rc = -1;
  2237.         break;
  2238.         }
  2239.         else if(rc == 17 || F_ON(F_SELECT_WO_CONFIRM,ps_global))
  2240.           /*
  2241.            * The 17 comes from folder_lister, which returns 17 if it
  2242.            * passes back multiple groups in the folders array.
  2243.            */
  2244.           break;
  2245.  
  2246.     }
  2247.         else if(rc == 3){
  2248.             help = help == NO_HELP ? h_news_subscribe : NO_HELP;
  2249.     }
  2250.     else if(rc == 1 || add_folder[0] == '\0'){
  2251.         rc = -1;
  2252.         break;
  2253.     }
  2254.     }
  2255.  
  2256.     free_folder_list(&subscribe_cntxt.folders);
  2257.  
  2258.     if(rc < 0){
  2259.     if(rc == -1)
  2260.       q_status_message(SM_ORDER, 0, 3, "Subscribe cancelled");
  2261.  
  2262.     return(NULL);
  2263.     }
  2264.  
  2265.     /*------ Actually do the subscription -----*/
  2266.     if(rc == 17){
  2267.     int i, n = 0, errors = 0;
  2268.  
  2269.     /* subscribe one at a time */
  2270.     for(i=0; folders[i]; i++){
  2271.         context_apply(tmp_20k_buf, subscribe_cntxt.context, folders[i]);
  2272.         full_name = cpystr(tmp_20k_buf+1);
  2273.         rc = (int)mail_subscribe_bboard(NULL, full_name);
  2274.         fs_give((void **)&full_name);
  2275.         if(rc == 0){
  2276.         /*
  2277.          * This message may not make it to the screen, because
  2278.          * a c-client message about the failure will be there.
  2279.          * Probably best not to string together a whole bunch of errors
  2280.          * if there is something wrong.
  2281.          */
  2282.         q_status_message1(errors?SM_INFO:SM_ORDER, errors ? 0 : 3, 3,
  2283.                   "Error subscribing to \"%s\"", folders[i]);
  2284.         errors++;
  2285.         }
  2286.         else{
  2287.         /*
  2288.          * Save for updating cursor on display.  Arbitrarily choose
  2289.          * the first one in the list for the cursor location.
  2290.          */
  2291.         if(n == 0)
  2292.           strcpy(add_folder, folders[i]);
  2293.  
  2294.         n++;
  2295.         /*---- Update the screen display data structures -----*/
  2296.         if(ALL_FOUND(cntxt)){  /* Not sure what this does... */
  2297.             if(cntxt->use & CNTXT_PSEUDO){
  2298.             folder_delete(0, cntxt->folders);
  2299.             cntxt->use &= ~CNTXT_PSEUDO;
  2300.             }
  2301.  
  2302.             folder_insert(-1, new_folder(folders[i]), cntxt->folders);
  2303.         }
  2304.         }
  2305.     }
  2306.  
  2307.     if(n == 0){
  2308.         q_status_message(SM_ORDER | SM_DING, 3, 5,
  2309.         "Subscriptions failed, subscribed to no new groups");
  2310.         add_folder = NULL;
  2311.     }
  2312.     else
  2313.       q_status_message3(SM_ORDER | (errors ? SM_DING : 0), errors ? 3 : 0,3,
  2314.           "Subscribed to %s new groups%s%s",
  2315.           comatose((long)n),
  2316.           errors ? ", failed on " : "",
  2317.           errors ? comatose((long)errors) : "");
  2318.  
  2319.     for(i=0; folders[i]; i++)
  2320.       fs_give((void **)&folders[i]);
  2321.  
  2322.     fs_give((void **)&folders);
  2323.     }
  2324.     else{
  2325.     context_apply(tmp_20k_buf, subscribe_cntxt.context, add_folder);
  2326.     full_name = cpystr(tmp_20k_buf+1);
  2327.     rc = (int)mail_subscribe_bboard(NULL, full_name);
  2328.     fs_give((void **)&full_name);
  2329.     if(rc == 0){
  2330.         q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2331.                   "Error subscribing to \"%s\"", add_folder);
  2332.         return(NULL);
  2333.     }
  2334.  
  2335.     /*---- Update the screen display data structures -----*/
  2336.     if(ALL_FOUND(cntxt)){  /* Not sure what this does... */
  2337.         if(cntxt->use & CNTXT_PSEUDO){
  2338.         folder_delete(0, cntxt->folders);
  2339.         cntxt->use &= ~CNTXT_PSEUDO;
  2340.         }
  2341.  
  2342.         folder_insert(-1, new_folder(add_folder), cntxt->folders);
  2343.     }
  2344.  
  2345.     q_status_message1(SM_ORDER, 0, 3, "Subscribed to \"%s\"", add_folder);
  2346.     }
  2347.  
  2348.     return(add_folder);
  2349. }
  2350.  
  2351.  
  2352.  
  2353. /*----------------------------------------------------------------------
  2354.       Rename folder
  2355.   
  2356.    Args: q_line     -- Screen line to prompt on
  2357.          index      -- index of folder in folder list to rename
  2358.          cntxt      -- collection of folders making up folder list
  2359.  
  2360.  Result: returns the new name of the folder, or NULL if nothing happened.
  2361.  
  2362.  When either the sent-mail or saved-message folders are renamed, immediately 
  2363. create a new one in their place so they always exist. The main loop above also
  2364. detects this and makes the rename look like an add of the sent-mail or
  2365. saved-messages folder. (This behavior may not be optimal, but it keeps things
  2366. consistent.
  2367.  
  2368.   ----*/
  2369. char *
  2370. rename_folder(q_line, index, cntxt)
  2371.      int        q_line, index;
  2372.      CONTEXT_S *cntxt;
  2373. {
  2374.     static char  new_foldername[MAXFOLDER+1];
  2375.     char        *folder, *prompt;
  2376.     HelpType     help;
  2377.     int          rc, ren_cur, cnt = 0;
  2378.     FOLDER_S    *new_f;
  2379.     MAILSTREAM  *ren_stream = NULL;
  2380.  
  2381.     dprint(4, (debugfile, "\n - rename folder -\n"));
  2382.  
  2383.     if(cntxt->type & FTYPE_BBOARD){
  2384.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  2385.              "Can't rename bulletin boards or news groups!");
  2386.     return(NULL);
  2387.     }
  2388.     else if(!ALL_FOUND(cntxt) || (cntxt->use & CNTXT_PSEUDO)){
  2389.     q_status_message1(SM_ORDER | SM_DING, 0, 3,
  2390.               "No folder selected to rename.  %s list.",
  2391.               ALL_FOUND(cntxt) ? "Empty" : "Expand");
  2392.     return(NULL);
  2393.     }
  2394.     else if((new_f = folder_entry(index, cntxt->folders))
  2395.         && strucmp(FLDR_NAME(new_f), ps_global->inbox_name) == 0) {
  2396.         q_status_message1(SM_ORDER | SM_DING, 3, 4,
  2397.               "Can't change special folder name \"%s\"",
  2398.               ps_global->inbox_name);
  2399.         return(NULL);
  2400.     }
  2401.     else if(new_f->nickname){ 
  2402.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  2403.              "Can't rename folder nicknames at this time!");
  2404.     return(NULL);
  2405.     }
  2406.  
  2407.     folder  = new_f->name;
  2408.     ren_cur = strcmp(folder, ps_global->cur_folder) == 0;
  2409.  
  2410.     prompt = "Rename folder to : ";
  2411.     help   = NO_HELP;
  2412.     strcpy(new_foldername, folder);
  2413.     while(1) {
  2414.         rc = optionally_enter(new_foldername, q_line, 0, MAXFOLDER, 1, 0,
  2415.                               prompt, NULL, help, 0);
  2416.         if(rc == 3) {
  2417.             help = help == NO_HELP ? h_oe_foldrename : NO_HELP;
  2418.             continue;
  2419.         }
  2420.  
  2421.     removing_trailing_white_space(new_foldername);
  2422.     removing_leading_white_space(new_foldername);
  2423.  
  2424.         if(rc == 0 && *new_foldername) {
  2425.         /* verify characters */
  2426.         if(!ps_global->show_dot_names && *new_foldername == '.'){
  2427.         if(cnt++ <= 0)
  2428.                   q_status_message(SM_ORDER,3,3,
  2429.             "Folder name can't begin with dot");
  2430.         else{
  2431.             NAMEVAL_S *feat;
  2432.             int i;
  2433.  
  2434.             for(i=0; (feat=feature_list(i))
  2435.                 && (feat->value != F_ENABLE_DOT_FOLDERS); i++)
  2436.               ;/* do nothing */
  2437.  
  2438.             q_status_message1(SM_ORDER,3,3,
  2439.               "Config feature \"%s\" enables names beginning with dot",
  2440.               feat && feat->name ? feat->name : "");
  2441.         }
  2442.  
  2443.                 display_message(NO_OP_COMMAND);
  2444.                 continue;
  2445.         }
  2446.  
  2447.         if(folder_index(new_foldername, cntxt->folders) >= 0){
  2448.                 q_status_message1(SM_ORDER,3,3, "Folder \"%s\" already exists",
  2449.                                   pretty_fn(new_foldername));
  2450.                 display_message(NO_OP_COMMAND);
  2451.                 continue;
  2452.             }
  2453.         }
  2454.  
  2455.         if(rc != 4) /* redraw */
  2456.           break;  /* no redraw */
  2457.  
  2458.     }
  2459.  
  2460.     if(rc==1 || new_foldername[0]=='\0' || strcmp(new_foldername, folder)==0){
  2461.         q_status_message(SM_ORDER, 0, 2, "Folder rename cancelled");
  2462.         return(0);
  2463.     }
  2464.  
  2465.     if(ren_cur && ps_global->mail_stream != NULL) {
  2466.         mail_close(ps_global->mail_stream);
  2467.         ps_global->mail_stream = NULL;
  2468.     }
  2469.  
  2470.     ren_stream = context_same_stream(cntxt->context, new_foldername,
  2471.                      ps_global->mail_stream);
  2472.  
  2473.     if(!ren_stream && ps_global->mail_stream != ps_global->inbox_stream)
  2474.       ren_stream = context_same_stream(cntxt->context, new_foldername,
  2475.                        ps_global->inbox_stream);
  2476.       
  2477.     if(rc = context_rename(cntxt->context,ren_stream,folder,new_foldername)){
  2478.     /* insert new name */
  2479.     new_f               = new_folder(new_foldername);
  2480.     new_f->prefix[0]    = '\0';
  2481.     new_f->msg_count    = 0;
  2482.     new_f->unread_count = 0;
  2483.     folder_insert(-1, new_f, cntxt->folders);
  2484.  
  2485.     if(strcmp(ps_global->VAR_DEFAULT_FCC, folder) == 0
  2486.        || strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER, folder) == 0) {
  2487.         /* renaming sent-mail or saved-messages */
  2488.         if(context_create(cntxt->context, NULL, folder)){
  2489.         q_status_message3(SM_ORDER,0,3,
  2490.              "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
  2491.                   folder, new_foldername,
  2492.                   pretty_fn(
  2493.                     (strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER,
  2494.                         folder) == 0)
  2495.                     ? ps_global->VAR_DEFAULT_SAVE_FOLDER
  2496.                     : ps_global->VAR_DEFAULT_FCC));
  2497.  
  2498.         }
  2499.         else{
  2500.         if((index = folder_index(folder, cntxt->folders)) >= 0)
  2501.           folder_delete(index, cntxt->folders); /* delete old struct */
  2502.  
  2503.         q_status_message1(SM_ORDER | SM_DING, 3, 4,
  2504.                   "Error creating new \"%s\"", folder);
  2505.  
  2506.         dprint(2, (debugfile, "Error creating \"%s\" in %s context\n",
  2507.                folder, cntxt->context));
  2508.         }
  2509.     }
  2510.     else{
  2511.         q_status_message2(SM_ORDER, 0, 3, "Folder \"%s\" renamed to \"%s\"",
  2512.                   pretty_fn(folder), pretty_fn(new_foldername));
  2513.  
  2514.         if((index = folder_index(folder, cntxt->folders)) >= 0)
  2515.           folder_delete(index, cntxt->folders); /* delete old struct */
  2516.     }
  2517.     }
  2518.  
  2519.     if(ren_cur) {
  2520.         /* No reopen the folder we just had open */
  2521.         do_broach_folder(new_foldername, cntxt);
  2522.     }
  2523.  
  2524.     return(rc ? new_foldername : NULL);
  2525. }
  2526.  
  2527.  
  2528.  
  2529. /*----------------------------------------------------------------------
  2530.    Confirm and delete a folder
  2531.  
  2532.    Args: index -- Index of folder in collection to remove
  2533.          cntxt -- The particular collection the folder's to be remove from
  2534.          mangled_header -- Pointer to flag to set if the the anchor line
  2535.                            needs updating (deleted the open folder)
  2536.  
  2537.  Result: return 0 if not delete, 1 if deleted.
  2538.  
  2539.  NOTE: Currently disallows deleting open folder...
  2540.   ----*/
  2541. int
  2542. delete_folder(index, cntxt, mangled_header)
  2543.     int        index, *mangled_header;
  2544.     CONTEXT_S *cntxt;
  2545. {
  2546.     char       *folder, *full_folder, ques_buf[MAX_SCREEN_COLS+1];
  2547.     MAILSTREAM *del_stream = NULL;
  2548.     FOLDER_S   *fp;
  2549.     int         ret, close_opened = 0;
  2550.  
  2551.     if(cntxt->type & FTYPE_BBOARD){
  2552.     static char fmt[] = "Really unsubscribe from \"%.*s\"";
  2553.          
  2554.         folder = folder_entry(index, cntxt->folders)->name;
  2555.     /* 4 is strlen("%.*s") */
  2556.         sprintf(ques_buf, fmt, sizeof(ques_buf) - (sizeof(fmt)-4), folder);
  2557.     
  2558.         ret = want_to(ques_buf, 'n', 'x', NO_HELP, 0, 0);
  2559.         switch(ret) {
  2560.           /* ^C */
  2561.           case 'x':
  2562.             Writechar(BELL, 0);
  2563.             /* fall through */
  2564.           case 'n':
  2565.             return(0);
  2566.         }
  2567.     
  2568.         dprint(2, (debugfile, "deleting folder \"%s\" in context \"%s\"\n",
  2569.            folder, cntxt->context));
  2570.  
  2571.         context_apply(tmp_20k_buf, cntxt->context, folder);
  2572.     full_folder = cpystr(tmp_20k_buf + 1);
  2573.     ret = (int)mail_unsubscribe_bboard(NULL, full_folder);
  2574.     fs_give((void **)&full_folder);
  2575.         if(ret == 0){
  2576.             q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2577.                   "Error unsubscribing from \"%s\"", folder);
  2578.             return(0);
  2579.         }
  2580.  
  2581.     return(1);
  2582.     }
  2583.  
  2584.     if(!folder_total(cntxt->folders)){
  2585.     q_status_message(SM_ORDER | SM_DING, 0, 4,
  2586.              "Empty folder collection.  No folder to delete!");
  2587.     return(0);
  2588.     }
  2589.  
  2590.     if(cntxt->use & CNTXT_INCMNG){
  2591.     if(ps_global->readonly_pinerc){
  2592.         q_status_message(SM_ORDER,3,5,
  2593.         "Deletion cancelled: config file not editable");
  2594.         return(0);
  2595.     }
  2596.     }
  2597.  
  2598.     fp     = folder_entry(index, cntxt->folders);
  2599.     folder = FLDR_NAME(fp);
  2600.     dprint(4, (debugfile, "=== delete_folder(%s) ===\n", folder));
  2601.  
  2602.     if(strucmp(folder, ps_global->inbox_name) == 0) {
  2603.     q_status_message1(SM_ORDER | SM_DING, 3, 4,
  2604.          "Can't delete special folder \"%s\".", ps_global->inbox_name);
  2605.     return(0);
  2606.     }
  2607.     else if(cntxt == ps_global->context_current
  2608.         && strcmp(folder, ps_global->cur_folder) == 0)
  2609.       close_opened++;
  2610.  
  2611.     sprintf(ques_buf, "Really delete \"%s\"%s", folder, 
  2612.         close_opened ? " (the currently open folder)" : "");
  2613.  
  2614.     if((ret=want_to(ques_buf, 'n', 'x', NO_HELP, 0, 0)) != 'y'){
  2615.     q_status_message(SM_ORDER,0,3, (ret == 'x') ? "Delete cancelled" 
  2616.                           : "No folder deleted");
  2617.     return(0);
  2618.     }
  2619.  
  2620.     dprint(2, (debugfile, "deleting folder \"%s\" (%s) in context \"%s\"\n",
  2621.            fp->name, fp->nickname ? fp->nickname : "", cntxt->context));
  2622.  
  2623.     /*
  2624.      * Use fp->name since "folder" may be a nickname...
  2625.      */
  2626.     if(close_opened){
  2627.     /*
  2628.      * There *better* be a stream, but check just in case.  Then
  2629.      * close it, NULL the pointer, and let do_broach_folder fixup
  2630.      * the rest...
  2631.      */
  2632.     if(ps_global->mail_stream){
  2633.         mail_close(ps_global->mail_stream);
  2634.         ps_global->mail_stream = NULL;
  2635.         *mangled_header = 1;
  2636.         do_broach_folder(ps_global->inbox_name, ps_global->context_list);
  2637.     }
  2638.     }
  2639.     else
  2640.       del_stream = context_same_stream(cntxt->context, fp->name,
  2641.                        ps_global->mail_stream);
  2642.  
  2643.     if(!del_stream && ps_global->mail_stream != ps_global->inbox_stream)
  2644.       del_stream = context_same_stream(cntxt->context, fp->name,
  2645.                        ps_global->inbox_stream);
  2646.  
  2647.     if(!((cntxt->use & CNTXT_INCMNG)
  2648.      && !folder_exists(cntxt->context, fp->name))
  2649.        && !context_delete(cntxt->context, del_stream, fp->name)){
  2650. /*
  2651.  * BUG: what if sent-mail or saved-messages????
  2652.  */
  2653.     q_status_message1(SM_ORDER,3,3,"Delete of \"%s\" Failed!", folder);
  2654.     return(0);
  2655.     }
  2656.  
  2657.     if(cntxt->use & CNTXT_INCMNG){
  2658.     char *p;
  2659.     int   i;
  2660.     for(i = 0; ps_global->USR_INCOMING_FOLDERS[i]; i++){
  2661.         /*
  2662.          * Quite a test, non?
  2663.          */
  2664.         if((p = srchstr(ps_global->USR_INCOMING_FOLDERS[i], fp->name))
  2665.            && p[strlen(fp->name)] == '\0'
  2666.            && (p == ps_global->USR_INCOMING_FOLDERS[i]
  2667.            || isspace((unsigned char)*(p-1)))){
  2668.         fs_give((void **)&(ps_global->USR_INCOMING_FOLDERS[i]));
  2669.         while(ps_global->USR_INCOMING_FOLDERS[i]
  2670.               = ps_global->USR_INCOMING_FOLDERS[i+1])
  2671.           i++;                /* rub it out */
  2672.         }
  2673.     }
  2674.  
  2675.     write_pinerc(ps_global);        /* save new element */
  2676.     }
  2677.  
  2678.     q_status_message1(SM_ORDER, 0,3,"Folder \"%s\" deleted!", folder);
  2679.     return(1);
  2680. }
  2681.  
  2682.  
  2683.  
  2684. /*----------------------------------------------------------------------
  2685.       Print the list of folders on paper
  2686.  
  2687.    Args: list    --  The current list of folders
  2688.          lens    --  The list of lengths of the current folders
  2689.          display --  The current folder display structure
  2690.  
  2691.  Result: list printed on paper
  2692.  
  2693. If the display list was created for 80 columns it is used, otherwise
  2694. a new list is created for 80 columns
  2695.  
  2696.   ----*/
  2697.  
  2698. void
  2699. print_folders(display)
  2700.     FSTATE_S  *display;
  2701. {
  2702.     int i, index, l;
  2703.     CONTEXT_S *context;
  2704.     FOLDER_S  *f;
  2705.     char       buf[256];
  2706.  
  2707.     if(ps_global->ttyo->screen_cols != 80)
  2708.       create_folder_display(display, 80);
  2709.  
  2710.     if(open_printer("folder list ") != 0)
  2711.       return;
  2712.  
  2713.     context = display->context_list;
  2714.     index   = i = 0;
  2715.     while(context){
  2716.     for(;i < context->d_line;i++) /* leading spaces */
  2717.       print_text(NEWLINE);
  2718.  
  2719.     memset((void *)tmp_20k_buf,LINECH, 80 * sizeof(char));
  2720.     tmp_20k_buf[80] = '\0';
  2721.     print_text(tmp_20k_buf);
  2722.     print_text(NEWLINE);
  2723.     i++;
  2724.  
  2725.     memset((void *)tmp_20k_buf,' ', 80 * sizeof(char));
  2726.     tmp_20k_buf[80] = '\0';
  2727.     if(context->use & CNTXT_INCMNG){
  2728.         i = strlen(context->label[0]);
  2729.         strncpy(tmp_20k_buf + max(40 - (i/2), 0), context->label[0], i);
  2730.     }
  2731.     else{
  2732.         sprintf(tmp_20k_buf + 80 + 2, 
  2733.             " %s-collection <%s>  %s", 
  2734.             (context->type & FTYPE_BBOARD) ? "News" : "Mail",
  2735.             context->label[0], 
  2736.             (context->use & CNTXT_SAVEDFLT)
  2737.                            ? "** Default for Saves **" : "");
  2738.         strncpy(tmp_20k_buf, 
  2739.             tmp_20k_buf + 80 + 2, 
  2740.             strlen(tmp_20k_buf + 80 + 2));
  2741.         strncpy(tmp_20k_buf + 80
  2742.                        - ((context->type & FTYPE_REMOTE) ? 9 : 8),
  2743.             (context->type & FTYPE_REMOTE)?"(Remote)":"(Local)",
  2744.             (context->type & FTYPE_REMOTE) ? 8 : 7);
  2745.     }
  2746.         print_text(tmp_20k_buf);
  2747.         print_text(NEWLINE);
  2748.         i++;
  2749.  
  2750.     memset((void *)tmp_20k_buf,LINECH,80 * sizeof(char));
  2751.     tmp_20k_buf[80] = '\0';
  2752.     print_text(tmp_20k_buf);
  2753.     print_text(NEWLINE);
  2754.     i++;
  2755.  
  2756.     for(i++; folder_total(context->folders) 
  2757.         && i < (int)folder_entry(0, context->folders)->d_line ; i++)
  2758.       print_text(NEWLINE);
  2759.  
  2760.     *tmp_20k_buf = '\0';
  2761.     for(index=0; index < folder_total(context->folders); index++){
  2762.         f = folder_entry(index, context->folders);
  2763.         if(f->d_col == 0){
  2764.         i++;
  2765.         strcat(tmp_20k_buf, NEWLINE);
  2766.         print_text(tmp_20k_buf);
  2767.         tmp_20k_buf[0] = '\0';
  2768.         }
  2769.  
  2770.         l = strlen(tmp_20k_buf);
  2771.         if(context->type & FTYPE_BBOARD)
  2772.           sprintf(buf, "%*s%s", 
  2773.               strlen(f->prefix) + max(0,((int)f->d_col - l)),
  2774.               f->prefix,
  2775.               f->name);
  2776.         else
  2777.           sprintf(buf, "%*s", f->name_len + max(0,((int)f->d_col - l)),
  2778.               FLDR_NAME(f));
  2779.         strcat(tmp_20k_buf, buf);
  2780.     }
  2781.  
  2782.     print_text(tmp_20k_buf);
  2783.     print_text(NEWLINE);
  2784.     context = context->next;
  2785.     }
  2786.  
  2787.     close_printer();
  2788.     if(ps_global->ttyo->screen_cols != 80)
  2789.       create_folder_display(display, ps_global->ttyo->screen_cols);
  2790. }
  2791.  
  2792.                      
  2793.  
  2794. /*----------------------------------------------------------------------
  2795.   Search folder list
  2796.  
  2797.    Args: fd       -- The folder display structure
  2798.          index    -- pointer to index of current folder (new folder if found)
  2799.          context  -- pointer to context of current folder (new folder if found)
  2800.          ask_line -- Screen line to prompt on
  2801.  
  2802.  Result: returns 
  2803.                 -1 if aborted
  2804.                  0 if NOT found
  2805.          1 if found
  2806.          2 if found and wrapped
  2807.   ----------------------------------------------------------------------*/
  2808. int
  2809. search_folders(fd, ask_line)
  2810.     FSTATE_S   *fd;
  2811.     int         ask_line;
  2812. {
  2813.     char            prompt[MAX_SEARCH+50], nsearch_string[MAX_SEARCH+1];
  2814.     HelpType        help = NO_HELP;
  2815.     CONTEXT_S      *t_context;
  2816.     FOLDER_S       *f;
  2817.     int             rc, t_index;
  2818.     static char     search_string[MAX_SEARCH+1];
  2819.     static ESCKEY_S folder_search_keys[] = { { 0, 0, "", "" },
  2820.                         {ctrl('Y'), 10, "^Y", "First Fldr"},
  2821.                         {ctrl('V'), 11, "^V", "Last Fldr"},
  2822.                         {-1, 0, NULL, NULL} };
  2823.  
  2824.     nsearch_string[0] = '\0';
  2825.     if(!folder_total((t_context = fd->context)->folders)){
  2826.     q_status_message(SM_ORDER | SM_DING, 0, 4,
  2827.              "Empty folder collection.  No folders to search!");
  2828.     return(0);
  2829.     }
  2830.  
  2831.     t_index           = fd->folder_index;
  2832.     sprintf(prompt, "Folder name to search for %s%s%s: ", 
  2833.         (*search_string == '\0') ? "" : "[", 
  2834.         search_string,
  2835.         (*search_string == '\0') ? "" : "] ");
  2836.  
  2837.     while(1) {
  2838.         rc = optionally_enter(nsearch_string, ask_line, 0, MAX_SEARCH, 1,
  2839.                   0, prompt, folder_search_keys, help,0);
  2840.         if(rc == 3) {
  2841.             help = help == NO_HELP ? h_oe_foldsearch : NO_HELP;
  2842.             continue;
  2843.         }
  2844.     else if(rc == 10){
  2845.         fd->context      = fd->context_list;
  2846.         fd->folder_index = 0;
  2847.         q_status_message(SM_ORDER, 0, 3, "Searched to First Folder.");
  2848.         return(3);
  2849.     }
  2850.     else if(rc == 11){
  2851.         for(fd->context = fd->context_list;
  2852.         fd->context->next; 
  2853.         fd->context = fd->context->next)
  2854.           ;
  2855.  
  2856.         fd->folder_index = (ALL_FOUND(fd->context)) 
  2857.                 ? folder_total(fd->context->folders) - 1: 0;
  2858.         q_status_message(SM_ORDER, 0, 3, "Searched to Last Folder.");
  2859.         return(3);
  2860.     }
  2861.  
  2862.         if(rc != 4)
  2863.           break;
  2864.     }
  2865.  
  2866.     if(rc == 1 || (search_string[0] == '\0' && nsearch_string[0] == '\0'))
  2867.       return(-1);            /* abort */
  2868.  
  2869.     if(nsearch_string[0] != '\0')
  2870.       strcpy(search_string, nsearch_string);
  2871.  
  2872.     /*----- Search the bottom half of list ------*/
  2873.     rc = 0;
  2874.     while(1){
  2875.     if(t_index + 1 >= folder_total(t_context->folders)){
  2876.         t_index = 0;
  2877.         if(!(t_context = t_context->next)){
  2878.         t_context = fd->context_list;     /* wrap the search */
  2879.         rc = 1;
  2880.         }
  2881.     }
  2882.     else
  2883.       t_index++;
  2884.  
  2885.     if(t_index == fd->folder_index && t_context == fd->context)
  2886.       return(0);
  2887.  
  2888.     f = folder_entry(t_index, t_context->folders);
  2889.         if(srchstr(FLDR_NAME(f), search_string)){
  2890.         fd->folder_index   = t_index;
  2891.         fd->context        = t_context;
  2892.         return(rc + 1);
  2893.     }
  2894.     }
  2895. }
  2896.  
  2897. #ifdef NEWBB
  2898. /*----------------------------------------------------------------------
  2899.   Clears the "NEW " prefix off all the news groups that have it and
  2900. sorts them back into their normal positions in the list.  Also, resets
  2901. the time to check new groups against.
  2902.  
  2903. Args: flist  -- The folder list display structure to clear and sort
  2904.  
  2905. Returns: nothing
  2906.  
  2907. Using ctime format for the last time checked is OK, Probably should
  2908. use RFC-822 date format.  The important thing is that it is ASCII and
  2909. that code exists to parse it and convert it to the correct format.
  2910.   ----*/
  2911. void
  2912. clear_new_groups(flist)
  2913.      void *flist;
  2914. {
  2915.     int i;
  2916.     long now;
  2917.  
  2918.  
  2919.     /*------ Set the time to check for new groups with to now -----*/
  2920.     now    = time(0);
  2921.     set_variable(V_NNTP_NEW_GROUP_TIME, ctime(&now), 0);
  2922.  
  2923.     /*---- Change the "NEW " prefixes in the folder list to "    " -----*/
  2924.     for(i = 0; i < folder_total(flist); i++) {
  2925.         if(strcmp(folder_entry(i, flist)->prefix, "NEW ") == 0) {
  2926.             strcpy(folder_entry(i, flist)->prefix, "    ");
  2927.         } else {
  2928.             /* Done cause all new ones are at the top */
  2929.             break;
  2930.         }
  2931.     }
  2932.  
  2933.     /*--- Put the list in order, those that were new no longer at top ---*/
  2934.     sort_folder_list(flist, compare_folders);
  2935. }
  2936.  
  2937.  
  2938.  
  2939. /*----------------------------------------------------------------------
  2940.    Convert a date in ctime(3) format to the format required by NNTP
  2941.   NEWGROUP command (YYMMDD HHMMSS). 
  2942.  
  2943. Args: date -- Date string in ctime format 
  2944.        
  2945. Returns: date in NNTP format in static buffer 
  2946.   ----*/
  2947. char *
  2948. ctime2nntp(date)
  2949.      char *date;
  2950. {
  2951.     struct date d;
  2952.     static char timebuf[40];
  2953.  
  2954.     parse_date(date, &d);
  2955.  
  2956.     sprintf(timebuf, "%02d%02d%02d %02d%02d%02d",
  2957.             d.year % 100, d.month, d.day, d.hour, d.minute, d.sec);
  2958.     return(timebuf);
  2959. }
  2960. #endif    
  2961.  
  2962.  
  2963. /*----------------------------------------------------------------------
  2964.       compare two names for qsort, case independent
  2965.  
  2966.    Args: pointers to strings to compare
  2967.  
  2968.  Result: integer result of strcmp of the names.  Uses simple 
  2969.          efficiency hack to speed the string comparisons up a bit.
  2970.  
  2971.   ----------------------------------------------------------------------*/
  2972. int
  2973. compare_names(x, y)
  2974.     const QSType *x, *y;
  2975. {
  2976.     char *a = *(char **)x, *b = *(char **)y;
  2977.     int r;
  2978. #if defined(DOS) || defined(OS2)
  2979. #define    STRCMP    strucmp
  2980. #define    CMPI    UCMPI
  2981. #else
  2982. #define    STRCMP    strcmp
  2983. #define    CMPI(X,Y)    ((X)[0] - (Y)[0])
  2984. #endif
  2985. #define    UCMPI(X,Y)    ((isupper((unsigned char)((X)[0]))    \
  2986.                 ? (X)[0] - 'A' + 'a' : (X)[0])    \
  2987.               - (isupper((unsigned char)((Y)[0]))    \
  2988.                 ? (Y)[0] - 'A' + 'a' : (Y)[0]))
  2989.  
  2990.     /*---- Inbox always sorts to the top ----*/
  2991.     if((UCMPI(b, ps_global->inbox_name)) == 0
  2992.        && strucmp(b, ps_global->inbox_name) == 0)
  2993.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : 1);
  2994.     else if(CMPI(b, ps_global->VAR_DEFAULT_FCC) == 0
  2995.         && STRCMP(b, ps_global->VAR_DEFAULT_FCC) == 0)
  2996.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : 1);
  2997.     else if(CMPI(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
  2998.         && STRCMP(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
  2999.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : 1);
  3000.     else if(UCMPI(a, ps_global->inbox_name) == 0
  3001.         && strucmp(a, ps_global->inbox_name) == 0)
  3002.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : -1);
  3003.     /*----- The sent-mail folder, is always next ---*/
  3004.     else if(CMPI(a, ps_global->VAR_DEFAULT_FCC) == 0
  3005.         && STRCMP(a, ps_global->VAR_DEFAULT_FCC) == 0)
  3006.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : -1);
  3007.     /*----- The saved-messages folder, is always next ---*/
  3008.     else if(CMPI(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
  3009.         && STRCMP(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
  3010.       return((CMPI(a, b) == 0 && STRCMP(a, b) == 0) ? 0 : -1);
  3011.     else
  3012.       return((r = CMPI(a, b)) ? r : STRCMP(a, b));
  3013. }
  3014.  
  3015.  
  3016.  
  3017. /*----------------------------------------------------------------------
  3018.   This code calculate the screen arrangement for the folders screen.
  3019. It fills in the line and col number for each entry in the list.
  3020.  
  3021. Args: f_list  -- The folder list
  3022.       f_list_size -- The number of entries in the folder list 
  3023.       screen_cols -- The number of columns on the screen
  3024.  
  3025.  
  3026. BUG - test this with one, two and three folders
  3027. Returns: The folder display structure
  3028.   ----*/
  3029.  
  3030. compare_sizes(f1, f2)
  3031.      const QSType *f1, *f2;
  3032. {
  3033.     return((*(struct folder **)f1)->name_len - 
  3034.            (*(struct folder **)f2)->name_len);
  3035. }
  3036.  
  3037. compare_folders(f1, f2)
  3038.      const QSType *f1, *f2;
  3039. {
  3040.     char *s1, *s2;
  3041.  
  3042.     s1 = (*(FOLDER_S **)f1)->name;
  3043.     s2 = (*(FOLDER_S **)f2)->name;
  3044.  
  3045.     return(compare_names(&s1, &s2));
  3046. }
  3047.  
  3048. #ifdef NEWBB
  3049. /*----------------------------------------------------------------------
  3050.    Folder comparison that puts those with "NEW " prefix at top of list
  3051.   ---*/
  3052. compare_folders_new(f1, f2)
  3053.      const QSType *f1, *f2;
  3054. {
  3055.     char *s1, *s2;
  3056.     int   is_new1, is_new2;
  3057.  
  3058.     s1      = (*((FOLDER_S **)(f1)))->name;
  3059.     s2      = (*((FOLDER_S **)(f2)))->name;
  3060.     is_new1 = strcmp(((*(FOLDER_S **)(f1)))->prefix, "NEW ") == 0;
  3061.     is_new2 = strcmp(((*(FOLDER_S **)(f2)))->prefix, "NEW ") == 0;
  3062.  
  3063.     if(!(is_new1 ^ is_new2))
  3064.       return(compare_names(&s1, &s2));
  3065.     else if(is_new1)
  3066.       return(-11);
  3067.     else
  3068.       return(1);
  3069. }
  3070. #endif
  3071.  
  3072. /*----------------------------------------------------------------------
  3073.    Calculate the arrangement on the screen. This fills in the rwo and column
  3074.  in the struct folders in the global Pine folder list. 
  3075.  
  3076. Args: fold_disp  -- folder menu state 
  3077.       screen_cols -- The width of the screen
  3078.  
  3079. Names are passed pre-sorted.
  3080.   ---*/
  3081.  
  3082. void
  3083. create_folder_display(fold_disp, screen_cols)
  3084.      FSTATE_S *fold_disp;
  3085.      int       screen_cols;
  3086. {
  3087.     register int       index;
  3088.     register FOLDER_S *f;
  3089.     CONTEXT_S         *c_list;
  3090.     int                length = 0, row, col, goal;
  3091.  
  3092.     if(!fold_disp){            /* what? */
  3093.     q_status_message(SM_ORDER,3,3,
  3094.         "Programmer BOTCH: No folder state struct!");
  3095.     return;
  3096.     }
  3097.  
  3098.     row                      = (ps_global->context_list->next) ? 1 : 0;
  3099.     c_list                   = fold_disp->context_list;
  3100.     fold_disp->display_cols  = screen_cols;
  3101.     fold_disp->display_rows  = ps_global->ttyo->screen_rows 
  3102.                                  - FOOTER_ROWS(ps_global)
  3103.                                  - HEADER_ROWS(ps_global);
  3104.     while(c_list != NULL){
  3105.     /*--- 
  3106.       Figure out the column width to use for display. What we want is a 
  3107.       column width that looks nice for most of the folder names, but
  3108.       not to make the columns super wide because of a few folder names
  3109.       of exceptional length. This is done by sorting the lengths of the
  3110.       existing folders and using a width that will suit 95% of the 
  3111.       entries. 
  3112.  
  3113.       The miminum columns width used is 20.
  3114.      ----*/
  3115.     if(!ALL_FOUND(c_list)){
  3116.         length = strlen(CLICKHERE);
  3117.     }
  3118.     else if(!folder_total(c_list->folders) || (c_list->use&CNTXT_PSEUDO)){
  3119.         length = strlen(c_list->use&CNTXT_NEWS ? CLICKHERETOONEWS
  3120.                            : CLICKHERETOO);
  3121.     }
  3122.     else if(F_ON(F_VERT_FOLDER_LIST, ps_global)){
  3123.         length = screen_cols; /* not used */
  3124.     }
  3125.     else{
  3126.         length = 0;        /* start from scratch */
  3127.         for(index = 0; index < folder_total(c_list->folders); index++){
  3128.         f = folder_entry(index, c_list->folders);
  3129.         length = max(length, (int)f->name_len
  3130.                     + (f->prefix ? strlen(f->prefix) : 0)
  3131.                     + 1);
  3132.         }
  3133.     }
  3134.  
  3135.     length = max(20, length);
  3136.  
  3137.  
  3138.     /*---- 
  3139.       Now we go through the display list and fill in the rows and columns.
  3140.       If the entry is just a text entry then it is centered. If it is
  3141.       a folder name it is fit in after the preceed one if possible. If 
  3142.       not it is put on the line boundrieds
  3143.          ---*/
  3144.  
  3145.     if(ps_global->context_list->next){
  3146.         /* leave a line for each label... */
  3147.         for(index = 0; c_list->label[index] != NULL; index++){
  3148.         if(index == 0)
  3149.           c_list->d_line = row;    /* remember which row to start on */
  3150.  
  3151.         row++;
  3152.         }
  3153.  
  3154.         row++;            /* one blank line after labels */
  3155.         row++;            /* one more blank line after labels */
  3156.     }
  3157.  
  3158.     /* then assign positions for each folder name */
  3159.     col = 0;
  3160.     if(!ALL_FOUND(c_list)){
  3161.         if(c_list->use & CNTXT_PSEUDO){
  3162.         f = folder_entry(0, c_list->folders);
  3163.         }
  3164.         else{
  3165.         f = new_folder(CLICKHERE);
  3166.         folder_insert(0, f, c_list->folders);
  3167.         c_list->use |= CNTXT_PSEUDO;
  3168.         }
  3169.  
  3170.         f->d_line = row;
  3171.         f->d_col  = max(0,
  3172.                          (screen_cols - (int)f->name_len-strlen(f->prefix))/2);
  3173.     }
  3174.     else if(!folder_total(c_list->folders) || (c_list->use&CNTXT_PSEUDO)){
  3175.         if(c_list->use & CNTXT_PSEUDO)
  3176.           folder_delete(0, c_list->folders); /* may be CLICKHERE */
  3177.  
  3178.         f = new_folder(c_list->use&CNTXT_NEWS ? CLICKHERETOONEWS
  3179.                           : CLICKHERETOO);
  3180.         folder_insert(0, f, c_list->folders);
  3181.         c_list->use |= CNTXT_PSEUDO; /* let others know entry's bogus */
  3182.         f->d_line    = row;
  3183.         f->d_col     = max(0, (screen_cols - (int)f->name_len -
  3184.                                    strlen(f->prefix))/2);
  3185.     }
  3186.     /* one folder per line */
  3187.     else if(F_ON(F_VERT_FOLDER_LIST, ps_global)){
  3188.         for(index = 0; index < folder_total(c_list->folders); index++){
  3189.         f    = folder_entry(index, c_list->folders);
  3190.         if(index)
  3191.           row++;
  3192.  
  3193.         f->d_line = (unsigned int) row;
  3194.         f->d_col  = (unsigned int) col;
  3195.         }
  3196.     }
  3197.     else{
  3198.         int plen;
  3199.  
  3200.         for(index = 0; index < folder_total(c_list->folders); index++){
  3201.         f    = folder_entry(index, c_list->folders);
  3202.         plen = f->prefix ? strlen(f->prefix) : 0;
  3203.  
  3204.         if(col + (int)f->name_len + plen >= screen_cols){
  3205.             col = 0;
  3206.             row++;
  3207.         }
  3208.  
  3209.         f->d_line = (unsigned int) row;
  3210.         f->d_col  = (unsigned int) col;
  3211.  
  3212.         for(goal = 0; goal < (int) f->name_len + plen;
  3213.                     goal += length)
  3214.           col += length;
  3215.         }
  3216.     }
  3217.  
  3218.     fold_disp->last_row = row++;
  3219.     row++;                /* add a blank line */
  3220.     row++;                /* add another blank line */
  3221.  
  3222.     c_list = c_list->next;     /* format the next section... */
  3223.     }
  3224. }
  3225.  
  3226.  
  3227.  
  3228. /*----------------------------------------------------------------------
  3229.       Format the given folder name for display for the user
  3230.  
  3231.    Args: folder -- The folder name to fix up
  3232.  
  3233. Not sure this always makes it prettier. It could do nice truncation if we
  3234. passed in a length. Right now it adds the path name of the mail 
  3235. subdirectory if appropriate.
  3236.  ----*/
  3237.       
  3238. char *
  3239. pretty_fn(folder)
  3240.      char *folder;
  3241. {
  3242.     static char  pfn[MAXFOLDER * 2 + 1];
  3243.     char        *p;
  3244.  
  3245. #if defined(DOS) || defined(OS2)
  3246.     if(!ps_global->show_folders_dir || *folder == '\\' || 
  3247. #else
  3248.     if(!ps_global->show_folders_dir || *folder == '/' || *folder == '~' ||
  3249. #endif
  3250.        *folder == '{' || *folder == '\0' 
  3251.        || !strucmp(folder, ps_global->inbox_name))  {
  3252.         if(ps_global->nr_mode) {
  3253.             if((p = strindex(folder, '}')) != NULL)
  3254.               return(p +1);
  3255.             else if((p = strindex(folder, '/')) != NULL) 
  3256.               return(p+1);
  3257.         else
  3258.               return(folder);
  3259.         }
  3260.     else if(!strucmp(folder, ps_global->inbox_name)){
  3261.         strcpy(pfn, ps_global->inbox_name);
  3262.         return(pfn);
  3263.     }
  3264.     else
  3265.           return(folder);
  3266.  
  3267.     } else {
  3268.         build_path(pfn, ps_global->VAR_MAIL_DIRECTORY, folder);
  3269.         return(pfn);
  3270.     }
  3271. }
  3272.  
  3273.  
  3274.  
  3275. /*----------------------------------------------------------------------
  3276.        Check to see if folder exists in given context
  3277.  
  3278.   Args: c_string -- context to check for folder in
  3279.         file  -- name of folder to check
  3280.  
  3281.  Result: returns 1 if the folder exists
  3282.                  0 if not
  3283.         -1 on error
  3284.  
  3285.   Uses mail_find to sniff out the existance of the requested folder.
  3286.   The context string is just here for convenience.  Checking for
  3287.   folder's existance within a given context is probably more efficiently
  3288.   handled outside this function for now using find_folders_in_context().
  3289.  
  3290.     ----*/
  3291. int
  3292. folder_exists(c_string, file)
  3293.     char *c_string, *file;
  3294. {
  3295.     char         fn[MAXPATH+1], host[MAXPATH+1], mbox[MAXPATH+1], *find_string;
  3296.     int          ourstream = 0, old_dot_state, old_inbox_state, we_cancel = 0;
  3297.     MAILSTREAM  *find_stream = NULL;
  3298.  
  3299.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  3300.  
  3301.     if(c_string && *c_string && context_isambig(file))
  3302.       context_apply(fn, c_string, file);
  3303.     else
  3304.       strcpy(fn, file);
  3305.  
  3306.     find_stream = mail_valid(NULL, fn, NULL) ? mail_open(NULL,fn,OP_PROTOTYPE)
  3307.                          : NULL;
  3308.     if(find_stream && mail_valid_net(fn, find_stream->dtb, host, mbox)){
  3309.     if(!(find_stream = same_stream(fn, ps_global->mail_stream))){
  3310.         if(!(find_stream = same_stream(fn, ps_global->inbox_stream))){
  3311.         char tmp[MAXPATH], options[MAXPATH], *p = fn, *p2;
  3312.         while(*p && *p != '}' && *p != '/')
  3313.           p++;
  3314.  
  3315.         options[0] = '\0';
  3316.         if(*p == '/'){
  3317.             p2 = options;
  3318.             while(*p != '}')
  3319.               *p2++ = *p++;
  3320.  
  3321.             *p2 = '\0';
  3322.         }
  3323.  
  3324.         sprintf(tmp, "%s{%s%s}", (fn[0] == '*') ? "*" : "", host,
  3325.             options);
  3326.         ourstream++;
  3327.         if(!(find_stream = mail_open(NULL, tmp, OP_HALFOPEN))){
  3328.             if(we_cancel)
  3329.               cancel_busy_alarm(-1);
  3330.  
  3331.             return(-1);    /* mail_open should've displayed error */
  3332.         }
  3333.         }
  3334.     }
  3335.  
  3336.     find_string = mbox;
  3337.     }
  3338.     else
  3339.       find_string = fn;
  3340.  
  3341.     find_folder_list          = NULL;
  3342.     find_folder_count          = 0L;
  3343.     old_dot_state          = ps_global->show_dot_names;
  3344.     ps_global->show_dot_names = 1;         /* look everywhere */
  3345.     old_inbox_state          = find_folder_inbox;
  3346.     find_folder_inbox          = 1;        /* including "inbox" */
  3347.     if(find_stream && find_stream->mailbox && find_stream->mailbox[0] == '*')
  3348.       context_find_all_bboard(NULL, find_stream, find_string);
  3349.     else
  3350.       context_find_all(NULL, find_stream, find_string);
  3351.  
  3352.     ps_global->show_dot_names = old_dot_state;
  3353.     find_folder_inbox          = old_inbox_state;
  3354.     if(ourstream)
  3355.       mail_close(find_stream);
  3356.  
  3357.     if(we_cancel)
  3358.       cancel_busy_alarm(-1);
  3359.  
  3360.     return(find_folder_count > 0L);
  3361. }
  3362.  
  3363.  
  3364.  
  3365. /*----------------------------------------------------------------------
  3366.        Check to see if folder exists in given context
  3367.  
  3368.   Args: stream -- pointer to stream to use or create
  3369.     cntxt -- context to check for folder in
  3370.         folder -- name of folder to check for recent messages
  3371.  
  3372.  Result: returns 1 if there are recent messages (and *stream assigned)
  3373.                  0 if not
  3374.  
  3375.     ----*/
  3376. int
  3377. folder_has_recent(stream, cntxt, folder)
  3378.     MAILSTREAM **stream;
  3379.     CONTEXT_S    *cntxt;
  3380.     FOLDER_S    *folder;
  3381. {
  3382.     int         rv, we_cancel = 0;
  3383.     char       *fn;
  3384.     char        msg_buf[MAX_SCREEN_COLS+1];
  3385.  
  3386.     if(folder)
  3387.       fn = FLDR_NAME(folder);
  3388.     else
  3389.       return(0);
  3390.  
  3391.     strcat(strncat(strcpy(msg_buf, "Checking "), fn, 50),
  3392.        " for recent messages");
  3393.     we_cancel = busy_alarm(1, msg_buf, NULL, 1);
  3394.  
  3395.     /* current folder can't have recent */
  3396.     rv = ((ps_global->context_current != ps_global->context_list
  3397.        || strcmp(ps_global->cur_folder, fn))
  3398.       && stream
  3399.       && ((*stream) = context_open(cntxt->context, *stream,
  3400.                        folder->name, OP_READONLY))
  3401.       && (*stream)->recent > 0L);
  3402.  
  3403.     if(we_cancel)
  3404.       cancel_busy_alarm(0);
  3405.  
  3406.     return(rv);
  3407. }
  3408.  
  3409.  
  3410.  
  3411. /*----------------------------------------------------------------------
  3412.  Initialize global list of contexts for folder collections.
  3413.  
  3414.  Interprets collections defined in the pinerc and orders them for
  3415.  pine's use.  Parses user-provided context labels and sets appropriate 
  3416.  use use flags and the default prototype for that collection. 
  3417.  (See find_folders for how the actual folder list is found).
  3418.  
  3419.   ----*/
  3420. void
  3421. init_folders(ps)
  3422.     struct pine *ps;
  3423. {
  3424.     CONTEXT_S  *tc, *top = NULL, **clist, *prime = NULL;
  3425.     FOLDER_S   *f;
  3426.     int         num = 1, i;
  3427.  
  3428.     clist = ⊤
  3429.  
  3430.     /*
  3431.      * If no incoming folders are config'd, but the user asked for
  3432.      * them via feature, make sure at least "inbox" ends up there...
  3433.      */
  3434.     if(F_ON(F_ENABLE_INCOMING, ps) && !ps->VAR_INCOMING_FOLDERS){
  3435.     ps->VAR_INCOMING_FOLDERS    = (char **)fs_get(2 * sizeof(char *));
  3436.     ps->VAR_INCOMING_FOLDERS[0] = cpystr(ps->inbox_name);
  3437.     ps->VAR_INCOMING_FOLDERS[1] = NULL;
  3438.     }
  3439.  
  3440.     /*
  3441.      * Build context that's a list of folders the user's defined
  3442.      * as receiveing new messages.  At some point, this should
  3443.      * probably include adding a prefix with the new message count.
  3444.      */
  3445.     if(ps->VAR_INCOMING_FOLDERS && ps->VAR_INCOMING_FOLDERS[0]
  3446.        && (tc = new_context("Incoming-Folders []"))){ /* fake new context */
  3447.     tc->use    &= ~CNTXT_NOFIND;     /* mark all entries found */
  3448.     tc->use    |= CNTXT_INCMNG;    /* mark this as incoming collection */
  3449.     tc->num     = 0;
  3450.     if(tc->label[0])
  3451.       fs_give((void **)&tc->label[0]);
  3452.  
  3453.     tc->label[0] = cpystr("Incoming Message Folders");
  3454.  
  3455.     *clist = tc;
  3456.     clist  = &tc->next;
  3457.  
  3458.     for(i = 0; ps->VAR_INCOMING_FOLDERS[i] ; i++){
  3459.         char *folder_string, *nickname;
  3460.  
  3461.         /*
  3462.          * Parse folder line for nickname and folder name.
  3463.          * No nickname on line is OK.
  3464.          */
  3465.         get_pair(ps->VAR_INCOMING_FOLDERS[i], &nickname, &folder_string,0);
  3466.  
  3467.         /*
  3468.          * Allow for inbox to be specified in the incoming list, but
  3469.          * don't let it show up along side the one magically inserted
  3470.          * above!
  3471.          */
  3472.         if(!folder_string || !strucmp(ps->inbox_name, folder_string)){
  3473.         if(folder_string)
  3474.           fs_give((void **)&folder_string);
  3475.  
  3476.         if(nickname)
  3477.           fs_give((void **)&nickname);
  3478.  
  3479.         continue;
  3480.         }
  3481.  
  3482.         f = new_folder(folder_string);
  3483.         fs_give((void **)&folder_string);
  3484.         if(nickname){
  3485.         if(strucmp(ps->inbox_name, nickname)){
  3486.             f->nickname = nickname;
  3487.             f->name_len = strlen(f->nickname);
  3488.         }
  3489.         else
  3490.           fs_give((void **)&nickname);
  3491.         }
  3492.  
  3493.         folder_insert(f->nickname 
  3494.               && (strucmp(f->nickname, ps->inbox_name) == 0)
  3495.                 ? -1 : folder_total(tc->folders),
  3496.               f, tc->folders);
  3497.     }
  3498.     }
  3499.  
  3500.     /*
  3501.      * Build list of folder collections.  Because of the way init.c
  3502.      * works, we're guaranteed at least a default.  Also write any
  3503.      * "bogus format" messages...
  3504.      */
  3505.     for(i = 0; ps->VAR_FOLDER_SPEC && ps->VAR_FOLDER_SPEC[i] ; i++){
  3506.     if(tc = new_context(ps->VAR_FOLDER_SPEC[i])){
  3507.         if(!prime){
  3508.         prime       = tc;
  3509.         prime->use |= (CNTXT_PRIME | CNTXT_SAVEDFLT);
  3510.         }
  3511.         else
  3512.           tc->use  |= CNTXT_SECOND;
  3513.  
  3514.         tc->num     = num++;            /* place in the list */
  3515.         *clist      = tc;                /* add it to list   */
  3516.         clist       = &tc->next;            /* prepare for next */
  3517.     }
  3518.     }
  3519.  
  3520.  
  3521.     /*
  3522.      * Whoah cowboy!!!  Guess we couldn't find a valid folder
  3523.      * collection, so fall back on default...
  3524.      */
  3525.     if(!prime){
  3526.     char buf[MAXPATH];
  3527.  
  3528.     build_path(buf, ps->VAR_MAIL_DIRECTORY, "[]");
  3529.     tc          = new_context(buf);
  3530.     tc->use    |= (CNTXT_PRIME | CNTXT_SAVEDFLT);
  3531.     tc->num     = num++;                /* place in the list */
  3532.     *clist      = tc;                /* add it to list   */
  3533.     clist       = &tc->next;            /* prepare for next */
  3534.     }
  3535.  
  3536.     /*
  3537.      * At this point, insert the INBOX mapping as the leading
  3538.      * folder entry of the first collection...
  3539.      */
  3540.     init_inbox_mapping(ps->VAR_INBOX_PATH, top);
  3541.  
  3542.     /*
  3543.      * If no news collections spec'd, but an nntp-server defined, 
  3544.      * fake a default news collection.  Check only "user" and "global"
  3545.      * nntp_server values as the "current" value is influenced by
  3546.      *  env vars and other news config files (see init.c)...
  3547.      */
  3548.     if(!ps->VAR_NEWS_SPEC
  3549.        && ((ps->USR_NNTP_SERVER
  3550.         && ps->USR_NNTP_SERVER[0])
  3551.        || (ps->GLO_NNTP_SERVER
  3552.            && ps->GLO_NNTP_SERVER[0]))){
  3553.     char buf[MAXPATH];
  3554.  
  3555.     ps->VAR_NEWS_SPEC    = (char **)fs_get(2 * sizeof(char *));
  3556.     sprintf(buf, "*{%s/nntp}[]", ps->VAR_NNTP_SERVER[0]);
  3557.     ps->VAR_NEWS_SPEC[0] = cpystr(buf);
  3558.     ps->VAR_NEWS_SPEC[1] = NULL;
  3559.     }
  3560.  
  3561.     /*
  3562.      * If news groups, loop thru list adding to collection list
  3563.      */
  3564.     for(i = 0; ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[i] ; i++){
  3565.     if(ps->VAR_NEWS_SPEC[i][0]
  3566.        && (tc = new_context(ps->VAR_NEWS_SPEC[i]))){
  3567.         tc->use    |= CNTXT_NEWS;
  3568.         tc->num     = num++;
  3569.         *clist      = tc;            /* add it to list   */
  3570.         clist       = &tc->next;        /* prepare for next */
  3571.     }
  3572.     }
  3573.  
  3574.     ps->context_list    = top;    /* init pointers */
  3575.     ps->context_current = (top->use & CNTXT_INCMNG) ? top->next : top;
  3576. #ifdef    DEBUG
  3577.     if(debug > 7 && do_debug(debugfile))
  3578.       dump_contexts(debugfile);
  3579. #endif
  3580. }
  3581.  
  3582.  
  3583.  
  3584. #ifdef    DEBUG
  3585. dump_contexts(fp)
  3586.     FILE *fp;
  3587. {
  3588.     FOLDER_S *f;
  3589.     int i = 0;
  3590.     CONTEXT_S *c = ps_global->context_list;
  3591.  
  3592.     while(fp && c != NULL){
  3593.     fprintf(fp, "\n***** context %s\n", c->context);
  3594.     for(i=0;c->label[i] != NULL;i++)
  3595.       fprintf(fp,"LABEL: %s\n", c->label[i]);
  3596.     
  3597.     for(i = 0; i < folder_total(c->folders); i++)
  3598.       fprintf(fp, "  %d) %s\n", i + 1, folder_entry(i,c->folders)->name);
  3599.     
  3600.     c = c->next;
  3601.     }
  3602. }
  3603. #endif
  3604.  
  3605.  
  3606. /*
  3607.  * Return malloc'd string containing only the context specifier given
  3608.  * a string containing the raw pinerc entry
  3609.  */
  3610. char *
  3611. context_string(s)
  3612.     char *s;
  3613. {
  3614.     CONTEXT_S *t = new_context(s);
  3615.     char      *rv = NULL;
  3616.     int           i;
  3617.  
  3618.     if(t){
  3619.     /*
  3620.      * Take what we want from context, then free the rest...
  3621.      */
  3622.     rv = t->context;
  3623.     t->context = NULL;            /* don't free it! */
  3624.     free_context(&t);
  3625.     }
  3626.  
  3627.     return(rv ? rv : cpystr(""));
  3628. }
  3629.  
  3630.  
  3631. /*
  3632.  *  Release resources associated with global context list
  3633.  */
  3634. void
  3635. free_folders()
  3636. {
  3637.     CONTEXT_S  *blast;
  3638.  
  3639.     while(blast = ps_global->context_list){
  3640.     ps_global->context_list = ps_global->context_list->next;
  3641.     free_context(&blast);            /* blast the context */
  3642.     }
  3643.  
  3644.     ps_global->context_current = NULL;
  3645. }
  3646.  
  3647.  
  3648. /*
  3649.  *  Release resources associated with the given context structure
  3650.  */
  3651. void
  3652. free_context(cntxt)
  3653.     CONTEXT_S **cntxt;
  3654. {
  3655.     char **labels;
  3656.  
  3657.     if((*cntxt)->context)
  3658.       fs_give((void **)&(*cntxt)->context);
  3659.     
  3660.     for(labels = (*cntxt)->label; *labels; labels++)
  3661.       fs_give((void **)labels);
  3662.     
  3663.     if((*cntxt)->nickname)
  3664.       fs_give((void **)&(*cntxt)->nickname);
  3665.  
  3666.     if((*cntxt)->folders)
  3667.       free_folder_list((void **)&(*cntxt)->folders);
  3668.  
  3669.     fs_give((void **)cntxt);
  3670. }
  3671.  
  3672.  
  3673. /*
  3674.  *
  3675.  */
  3676. void
  3677. init_inbox_mapping(path, cntxt)
  3678.      char      *path;
  3679.      CONTEXT_S *cntxt;
  3680. {
  3681.     FOLDER_S *f;
  3682.  
  3683.     /*
  3684.      * If mapping already exists, blast it and replace it below...
  3685.      */
  3686.     if((f = folder_entry(0, cntxt->folders))
  3687.        && f->nickname && !strcmp(f->nickname, ps_global->inbox_name))
  3688.       folder_delete(0, cntxt->folders);
  3689.  
  3690.     if(path){
  3691.     f = new_folder(path);
  3692.     f->nickname = cpystr(ps_global->inbox_name);
  3693.     f->name_len = strlen(f->nickname);
  3694.     }
  3695.     else
  3696.       f = new_folder(ps_global->inbox_name);
  3697.  
  3698.     folder_insert(0, f, cntxt->folders);
  3699. }
  3700.  
  3701.  
  3702. /*
  3703.  * returns with the folder's type's set
  3704.  *
  3705.  * WARNING: c-client naming knowledge is hardcoded here!
  3706.  *          This is based on our understanding of c-client naming 
  3707.  *        conventions:
  3708.  *
  3709.  *        leading '*' means local or remote bboard (news)
  3710.  *        leading '{' means remote access
  3711.  *        {XXX/nntp}  means remote nntp access
  3712.  *        {XXX/imap}  means remote imap access
  3713.  *        {XXX/anonymous}  means anonymous access
  3714. BUG? look into using mail.c:mail_valid_net()
  3715.  */
  3716. void
  3717. set_ftype(context, flags)
  3718.     char           *context;
  3719.     unsigned short *flags;
  3720. {
  3721.     char *p, tmp[MAXPATH];
  3722.     long  i;
  3723.  
  3724.     *flags = 0;                    /* clear flags */
  3725.  
  3726.     if(!context || *context == '\0')
  3727.       return;
  3728.  
  3729.     if(*context == '*'){
  3730.     *flags |= FTYPE_BBOARD;
  3731.     context++;
  3732.     }
  3733.  
  3734.     if(*context == '{' && context[1] && context[1] != '}' 
  3735.        && (p = strindex(context, '}'))){
  3736.     *flags |= FTYPE_REMOTE;
  3737.     i = p - context;
  3738.     strncpy(tmp, context, (int)i);
  3739.     tmp[i] = '\0';
  3740.     if(*p == '*')
  3741.       *flags |= FTYPE_BBOARD;
  3742.  
  3743.     if((p = strindex(tmp, '/')) && strucmp(p+1, "nntp") == 0)
  3744.       *flags |= FTYPE_OLDTECH;
  3745.  
  3746.     if(p && strucmp(p+1, "anonymous") == 0)
  3747.       *flags |= FTYPE_ANON;
  3748.     }
  3749.     else
  3750.       *flags |= FTYPE_LOCAL;        /* if it's not remote... */
  3751. }
  3752.  
  3753.  
  3754.  
  3755. /*
  3756.  * new_context - creates and fills in a new context structure, leaving
  3757.  *               blank the actual folder list (to be filled in later as
  3758.  *               needed).  Also, parses the context string provided 
  3759.  *               picking out any user defined label.  Context lines are
  3760.  *               of the form:
  3761.  *
  3762.  *               [ ["] <string> ["] <white-space>] <context>
  3763.  *
  3764.  */
  3765. CONTEXT_S *
  3766. new_context(cntxt_string)
  3767.     char *cntxt_string;
  3768. {
  3769.     CONTEXT_S  *c;
  3770.     char        host[MAXPATH], rcontext[MAXPATH], *nickname = NULL,
  3771.         *c_string = NULL, *p;
  3772.  
  3773.     /*
  3774.      * do any context string parsing (like splitting user-supplied 
  3775.      * label from actual context)...
  3776.      */
  3777.     get_pair(cntxt_string, &nickname, &c_string, 0);
  3778.  
  3779.     if(p = context_digest(c_string, NULL, host, rcontext, NULL)){
  3780.     q_status_message2(SM_ORDER | SM_DING, 3, 4,
  3781.               "Bad context, %s : %s", p, c_string);
  3782.     if(nickname)
  3783.       fs_give((void **)&nickname);
  3784.  
  3785.     return(NULL);
  3786.     }
  3787.  
  3788.     c = (CONTEXT_S *) fs_get(sizeof(CONTEXT_S)); /* get new context   */
  3789.     memset((void *) c, 0, sizeof(CONTEXT_S));    /* and initialize it */
  3790.     set_ftype(c_string, &(c->type));
  3791.  
  3792.     if(c->label[0] == NULL){
  3793.     if(!nickname){            /* make one up! */
  3794.         sprintf(tmp_20k_buf, "%s%s%s",
  3795.             (c->type & FTYPE_BBOARD) ? "News" : rcontext, 
  3796.             (c->type & FTYPE_REMOTE) ? " on " : "",
  3797.             (c->type & FTYPE_REMOTE) ? host : "");
  3798.         c->label[0]   = cpystr(tmp_20k_buf);
  3799.     }
  3800.     else
  3801.       c->label[0] = nickname;
  3802.     }
  3803.  
  3804.     c->context = c_string;
  3805.     c->use     = CNTXT_NOFIND;        /* do find later! */
  3806.     c->folders = new_folder_list();
  3807.  
  3808.  
  3809.     dprint(2, (debugfile, "Context %s type:%s%s%s%s%s%s\n", c->context,
  3810.            (c->type&FTYPE_LOCAL)   ? " LOCAL"   : "",
  3811.            (c->type&FTYPE_REMOTE)  ? " REMOTE"  : "",
  3812.            (c->type&FTYPE_SHARED)  ? " SHARED"  : "",
  3813.            (c->type&FTYPE_BBOARD)  ? " BBOARD"  : "",
  3814.            (c->type&FTYPE_OLDTECH) ? " OLDTECH"  : "",
  3815.            (c->type&FTYPE_ANON)    ? " ANON" : ""));
  3816.  
  3817.     return(c);
  3818. }
  3819.  
  3820.  
  3821.  
  3822. /*
  3823.  * find_folders_in_context - find the folders associated with the given context
  3824.  *                     and search pattern.  If the search pattern is not
  3825.  *                     the wild card ("*"), then some subset of all folders
  3826.  *                     is specified. So, don't mark context as completely
  3827.  *                     searched, but do set the bit to avoid recursive
  3828.  *                     calls...
  3829.  *
  3830.  *        If no search_string, we're being called to search the entire
  3831.  *        view within the given context.
  3832.  *
  3833.  *    NOTE: The first arg, "stream", is here for efficiency.
  3834.  *          If it's set, then the caller want's the stream we opened
  3835.  *          for the context_find(), so don't close it when we leave.
  3836.  *          If we don't use it and it's set, mail_close and NULL it.
  3837.  *          If we use another stream and it's set, close the old one
  3838.  *          and reset it to the new one.  It's up to the caller to
  3839.  *          make sure it get's closed properly.
  3840.  *
  3841.  */
  3842. void
  3843. find_folders_in_context(stream, context, search_string)
  3844.     MAILSTREAM **stream;
  3845.     CONTEXT_S    *context;
  3846.     char    *search_string;
  3847. {
  3848.     char  host[MAXPATH], rcontext[MAXPATH], view[MAXPATH],
  3849.          *search_context, *rv;
  3850.     int   local_open = 0, we_cancel = 0;
  3851.  
  3852.     if((context->use&CNTXT_NOFIND) == 0 || (context->use&CNTXT_PARTFIND))
  3853.       return;                /* find already done! */
  3854.  
  3855.     if(rv = context_digest(context->context, NULL, NULL, rcontext, view)){
  3856.     q_status_message2(SM_ORDER | SM_DING, 3, 3, "Bad context, %s : %s",
  3857.               rv, context->context);
  3858.     return;
  3859.     }
  3860.  
  3861.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  3862.  
  3863.     if(!search_string || (search_string[0]=='*' && search_string[1]=='\0')){
  3864.     context->use &= ~CNTXT_NOFIND;    /* let'em know we tried  */
  3865.     search_string = view;        /* return full view of context */
  3866.     }
  3867.     else
  3868.       context->use |= CNTXT_PARTFIND;    /* or are in a partial find */
  3869.  
  3870.     /* let context_mailbox know context! */
  3871.     current_context   = context->context;
  3872.     find_folder_list  = context->folders;
  3873.  
  3874.     dprint(7, (debugfile, "find_folders_in_context: %s\n",
  3875.            context->context));
  3876.  
  3877.     /*
  3878.      * Here's where we have to be careful.  C-client requires different
  3879.      * search strings and inconsistently returns results based on 
  3880.      * what sort of technology you're interested in... (would be nice to 
  3881.      * see this cleaned up)
  3882.      *
  3883.      *     Type      Search           Search returns
  3884.      *     ----      ------           --------------
  3885.      * local mail    mail/*           /usr/staff/mikes/mail/foo
  3886.      * imap mail     {remote}mail/*   nothing (unless {} in mboxlist names)
  3887.      * imap mail     mail/*           {remote}mail/foo
  3888.      * local news    *                foo.bar (if news spool local!)
  3889.      * imap news     *                ????
  3890.      * nntp news     *                foo.bar
  3891.      * nntp news     [host]*          [host]foo.bar
  3892.      *
  3893.      */
  3894.  
  3895.     find_folder_stream = NULL;
  3896.     if((context->type)&FTYPE_REMOTE){
  3897.     char *stream_name;
  3898.     search_context = rcontext;
  3899.  
  3900.     sprintf(tmp_20k_buf, "%.*s}",
  3901.         strindex(context->context, '}') - context->context, 
  3902.         context->context);
  3903.  
  3904.     stream_name = cpystr(tmp_20k_buf);
  3905.  
  3906.     /*
  3907.      * Try using a stream we've already got open...
  3908.      */
  3909.     if(stream && *stream
  3910.        && !(find_folder_stream = same_stream(stream_name, *stream))){
  3911.         mail_close(*stream);
  3912.         *stream = NULL;
  3913.     }
  3914.  
  3915.     if(!find_folder_stream)
  3916.       find_folder_stream = same_stream(stream_name,
  3917.                        ps_global->mail_stream);
  3918.  
  3919.     if(!find_folder_stream 
  3920.        && ps_global->mail_stream != ps_global->inbox_stream)
  3921.       find_folder_stream = same_stream(stream_name,
  3922.                        ps_global->inbox_stream);
  3923.  
  3924.     /* gotta open a new one */
  3925.     if(!find_folder_stream){
  3926.         if((context->type&FTYPE_OLDTECH) && !(context->use&CNTXT_FINDALL)){
  3927.         find_folder_stream = mail_open(NULL, stream_name,OP_PROTOTYPE);
  3928.         }
  3929.         else{
  3930.         local_open++;
  3931.         find_folder_stream = mail_open(NULL, stream_name, OP_HALFOPEN);
  3932.         if(stream)
  3933.           *stream = find_folder_stream;
  3934.         }
  3935.     }
  3936.  
  3937.     dprint(find_folder_stream ? 7 : 1,
  3938.            (debugfile, "find_folders: mail_open(%s) %s.\n",
  3939.         stream_name, find_folder_stream ? "OK" : "FAILED"));
  3940.     fs_give((void **)&stream_name);
  3941.     if(!find_folder_stream){
  3942.         context->use &= ~CNTXT_PARTFIND;    /* unset partial find bit */
  3943.         if(we_cancel)
  3944.           cancel_busy_alarm(-1);
  3945.  
  3946.         return;
  3947.     }
  3948.     }
  3949.     else{
  3950.     search_context = rcontext;
  3951.     if(stream && *stream){
  3952.         mail_close(*stream);
  3953.         *stream = NULL;
  3954.     }
  3955.     }
  3956.  
  3957.     ps_global->show_dot_names = F_ON(F_ENABLE_DOT_FOLDERS, ps_global);
  3958.  
  3959.     if(context->type & FTYPE_BBOARD){            /* get the list */
  3960. #ifdef NEWBB
  3961.         if(context->use & CNTXT_NEWBB) 
  3962.           context_find_new_bboard(search_context, find_folder_stream,
  3963.                              search_string,
  3964.                              ctime2nntp(ps_global->VAR_NNTP_NEW_GROUP_TIME));
  3965.         else
  3966. #endif
  3967.         if(context->use & CNTXT_FINDALL)
  3968.           context_find_all_bboard(search_context, find_folder_stream,
  3969.                                search_string);
  3970.         else 
  3971.        context_find_bboards(search_context, find_folder_stream,
  3972.                                  search_string);
  3973.  
  3974.         if((F_OFF(F_READ_IN_NEWSRC_ORDER,ps_global) ||
  3975.             context->use & CNTXT_FINDALL)
  3976. #ifdef NEWBB
  3977.           && !(context->use & CNTXT_NEWBB)
  3978. #endif
  3979.             )
  3980.            sort_folder_list(context->folders, compare_folders);
  3981.     }
  3982.     else{
  3983.     /*
  3984.      * For now use find_all as there's no distinguishing in pine
  3985.      * between subscribed and not with regard to mailboxes.  At some
  3986.      * point we may want to use the subscription mechanism to help 
  3987.      * performance, but implicit subscription needs to be added and 
  3988.      * folders created outside pine won't be automatically visible
  3989.      *
  3990.      * THOUGHT: the first find as pine starts does a find_all and then  
  3991.      * a find then reconciles the two automatically subscribing
  3992.      * any new folders.  We can then easily rebuild folder list on 
  3993.      * every folder menu access much cheaper (locally anyway) 
  3994.      * (probably only necessary to rebuild the section that has
  3995.      * to do with the current context)
  3996.      */
  3997.     context_find_all(search_context, find_folder_stream, search_string);
  3998.     }
  3999.  
  4000.     if(local_open && !stream)
  4001.       mail_close(find_folder_stream);
  4002.  
  4003.     context->use &= ~CNTXT_PARTFIND;    /* unset partial find bit */
  4004.     if(we_cancel)
  4005.       cancel_busy_alarm(-1);
  4006. }
  4007.  
  4008.  
  4009.  
  4010. /*
  4011.  * free_folders_in_context - loop thru the context's lists of folders
  4012.  *                     clearing all entries without nicknames
  4013.  *                     (as those were user provided) AND reset the 
  4014.  *                     context's find flag.
  4015.  *
  4016.  * NOTE: if fetched() information (e.g., like message counts come back
  4017.  *       in bboard collections), we may want to have a check before
  4018.  *       executing the loop and setting the FIND flag.
  4019.  */
  4020. void
  4021. free_folders_in_context(cntxt)
  4022.     CONTEXT_S *cntxt;
  4023. {
  4024.     int n, i;
  4025.  
  4026.     /*
  4027.      * In this case, don't blast the list as it was given to us by the
  4028.      * user and not the result of a mail_find* call...
  4029.      */
  4030.     if(cntxt->use & CNTXT_INCMNG)
  4031.       return;
  4032.  
  4033.     for(n = folder_total(cntxt->folders), i = 0; n > 0; n--)
  4034.       if(folder_entry(i, cntxt->folders)->nickname)
  4035.     i++;                /* entry wasn't a FIND result */
  4036.       else
  4037.     folder_delete(i, cntxt->folders);
  4038.  
  4039.     cntxt->use |= CNTXT_NOFIND;        /* do find next time...  */
  4040.     cntxt->use &= ~CNTXT_PSEUDO;    /* or add the fake entry */
  4041. }
  4042.  
  4043.  
  4044. /*
  4045.  * default_save_context - return the default context for saved messages
  4046.  */
  4047. CONTEXT_S *
  4048. default_save_context(cntxt)
  4049.     CONTEXT_S *cntxt;
  4050. {
  4051.     while(cntxt)
  4052.       if((cntxt->use) & CNTXT_SAVEDFLT)
  4053.     return(cntxt);
  4054.       else
  4055.     cntxt = cntxt->next;
  4056.  
  4057.     return(NULL);
  4058. }
  4059.  
  4060.  
  4061.  
  4062. /*----------------------------------------------------------------------
  4063.     Update the folder display structure for the number of unread
  4064.  messages for a news group
  4065.  
  4066. Args: stream   -- open mail stream to count unread messages on
  4067.       f_struct -- pointer to the structure to update
  4068.  
  4069. This is called when going into the folders screen or when closing a
  4070. news group to update the string that is displayed showing the number
  4071. of unread messages. The stream and f_struct passed in better be for
  4072. the same folder or things will get a little confused. 
  4073.  
  4074. This can also be a little slow because count_flagged() can be slow
  4075. due to current performance of the news driver.
  4076.  ----*/
  4077. void
  4078. update_news_prefix(stream, f_struct)
  4079.      MAILSTREAM *stream;
  4080.      struct folder *f_struct;
  4081. {
  4082.    int n;
  4083.  
  4084.    n = count_flagged(stream, "UNSEEN");
  4085.    sprintf(f_struct->prefix, "%4.4s ",  n ? int2string(n)  : "");
  4086. }
  4087.  
  4088.  
  4089.  
  4090. /*
  4091.  * folder_complete - foldername completion routine
  4092.  *              returns:
  4093.  *
  4094.  *   Result: returns 0 if the folder doesn't have a unique completetion
  4095.  *                   1 if so, and replaces name with completion
  4096.  */
  4097. folder_complete(context, name)
  4098.     CONTEXT_S *context;
  4099.     char      *name;
  4100. {
  4101.     int  i, match = -1, did_find;
  4102.     char tmp[MAXFOLDER], tmpname[MAXFOLDER], *a, *b; 
  4103.     FOLDER_S *f;
  4104. #if defined(DOS) || defined(OS2)
  4105. #define    STRUCMP    struncmp         /* ignore case under DOS */
  4106. #else
  4107. #define    STRUCMP    strncmp
  4108. #endif
  4109.     
  4110.     if(*name == '\0' || !context_isambig(name))
  4111.       return(0);
  4112.  
  4113.     if(did_find = !ALL_FOUND(context)){
  4114.     sprintf(tmpname, "%s*", name);
  4115.     find_folders_in_context(NULL, context, tmpname);
  4116.     }
  4117.     else if(context->use & CNTXT_PSEUDO)
  4118.       return(0);            /* no folders to complete */
  4119.  
  4120.     *tmp = '\0';            /* find uniq substring */
  4121.     for(i = 0; i < folder_total(context->folders); i++){
  4122.     f = folder_entry(i, context->folders);
  4123.     if(STRUCMP(name, FLDR_NAME(f), strlen(name)) == 0){
  4124.         if(match != -1){        /* oh well, do best we can... */
  4125.         a = FLDR_NAME(f);
  4126.         if(match >= 0){
  4127.             f = folder_entry(match, context->folders);
  4128.             strcpy(tmp, FLDR_NAME(f));
  4129.         }
  4130.  
  4131.         match = -2;
  4132.         b     = tmp;        /* remember largest common text */
  4133.         while(*a && *b && *a == *b)
  4134.           *b++ = *a++;
  4135.  
  4136.         *b = '\0';
  4137.         }
  4138.         else        
  4139.           match = i;        /* bingo?? */
  4140.     }
  4141.     }
  4142.  
  4143.     if(match >= 0){            /* found! */
  4144.     f = folder_entry(match, context->folders);
  4145.     strcpy(name, FLDR_NAME(f));
  4146.     }
  4147.     else if(match == -2)        /* closest we could find */
  4148.       strcpy(name, tmp);
  4149.  
  4150.     if(did_find){
  4151.     if(context->use & CNTXT_PSEUDO){
  4152.         while(folder_total(context->folders) > 1)
  4153.           folder_delete(strcmp(folder_entry(0, context->folders)->name,
  4154.                    CLICKHERE) ? 0 : 1,
  4155.                 context->folders);
  4156.     }
  4157.     else
  4158.       free_folders_in_context(context);
  4159.     }
  4160.  
  4161.     return((match >= 0) ? 1 : 0);
  4162. }
  4163.  
  4164.  
  4165. /*
  4166.  *           FOLDER MANAGEMENT ROUTINES
  4167.  */
  4168.  
  4169.  
  4170. /*
  4171.  * Folder List Structure - provides for two ways to access and manage
  4172.  *                         folder list data.  One as an array of pointers
  4173.  *                         to folder structs or
  4174.  */
  4175. typedef struct folder_list {
  4176.     unsigned   used;
  4177.     unsigned   allocated;
  4178. #ifdef    DOSXXX
  4179.     FILE      *folders;        /* tmpfile of binary */
  4180. #else
  4181.     FOLDER_S **folders;        /* array of pointers to folder structs */
  4182. #endif
  4183. } FLIST;
  4184.  
  4185. #define FCHUNK  64
  4186.  
  4187.  
  4188. /*
  4189.  * new_folder_list - return a piece of memory suitable for attaching 
  4190.  *                   a list of folders...
  4191.  *
  4192.  */
  4193. void *
  4194. new_folder_list()
  4195. {
  4196.     FLIST *flist = (FLIST *) fs_get(sizeof(FLIST));
  4197.     flist->folders = (FOLDER_S **) fs_get(FCHUNK * sizeof(FOLDER_S *));
  4198.     memset((void *)flist->folders, 0, (FCHUNK * sizeof(FOLDER_S *)));
  4199.     flist->allocated = FCHUNK;
  4200.     flist->used      = 0;
  4201.     return((void *)flist);
  4202. }
  4203.  
  4204.  
  4205.  
  4206. /*
  4207.  * new_folder - return a brand new folder entry, with the given name
  4208.  *              filled in.
  4209.  *
  4210.  * NOTE: THIS IS THE ONLY WAY TO PUT A NAME INTO A FOLDER ENTRY!!!
  4211.  * STRCPY WILL NOT WORK!!!
  4212.  */
  4213. FOLDER_S *
  4214. new_folder(name)
  4215.     char *name;
  4216. {
  4217. #ifdef    DOSXXX
  4218. #else
  4219.     FOLDER_S *tmp;
  4220.     size_t    l = strlen(name);
  4221.  
  4222.     tmp = (FOLDER_S *)fs_get(sizeof(FOLDER_S) + (l * sizeof(char)));
  4223.     memset((void *)tmp, 0, sizeof(FOLDER_S));
  4224.     strcpy(tmp->name, name);
  4225.     tmp->name_len = (unsigned char) l;
  4226.     return(tmp);
  4227. #endif
  4228. }
  4229.  
  4230.  
  4231.  
  4232. /*
  4233.  * folder_entry - folder struct access routine.  Permit reference to
  4234.  *                folder structs via index number. Serves two purposes:
  4235.  *            1) easy way for callers to access folder data 
  4236.  *               conveniently
  4237.  *            2) allows for a custom manager to limit memory use
  4238.  *               under certain rather limited "operating systems"
  4239.  *               who shall renameless, but whose initials are DOS
  4240.  *
  4241.  *
  4242.  */
  4243. FOLDER_S *
  4244. folder_entry(i, flist)
  4245.     int   i;
  4246.     void *flist;
  4247. {
  4248. #ifdef    DOSXXX
  4249.     /*
  4250.      * Manage entries within a limited amount of core (what a drag).
  4251.      */
  4252.  
  4253.     fseek((FLIST *)flist->folders, i * sizeof(FOLDER_S) + MAXPATH, 0);
  4254.     fread(&folder, sizeof(FOLDER_S) + MAXPATH, (FLIST *)flist->folders);
  4255. #else
  4256.     return((i >= ((FLIST *)flist)->used) ? NULL:((FLIST *)flist)->folders[i]);
  4257. #endif
  4258. }
  4259.  
  4260.  
  4261.  
  4262. /*
  4263.  * free_folder_list - release all resources associated with the given 
  4264.  *                    folder list
  4265.  */
  4266. void
  4267. free_folder_list(flist)
  4268.     void **flist;
  4269. {
  4270. #ifdef    DOSXXX
  4271.     fclose((*((FLIST **)flist))->folders);     /* close folder tmpfile */
  4272. #else
  4273.     register int i = (*((FLIST **)flist))->used;
  4274.  
  4275.     while(i--){
  4276.     if((*((FLIST **)flist))->folders[i]->nickname)
  4277.       fs_give((void **)&(*((FLIST **)flist))->folders[i]->nickname);
  4278.  
  4279.     fs_give((void **)&((*((FLIST **)flist))->folders[i]));
  4280.     }
  4281.  
  4282.     fs_give((void **)&((*((FLIST **)flist))->folders));
  4283. #endif
  4284.     fs_give(flist);
  4285. }
  4286.  
  4287.  
  4288.  
  4289. /*
  4290.  * return the number of folders associated with the given folder list
  4291.  */
  4292. int
  4293. folder_total(flist)
  4294.     void *flist;
  4295. {
  4296.     return((int)((FLIST *)flist)->used);
  4297. }
  4298.  
  4299.  
  4300. /*
  4301.  * return the index number of the given name in the given folder list
  4302.  */
  4303. int
  4304. folder_index(name, flist)
  4305.     char *name;
  4306.     void *flist;
  4307. {
  4308.     register  int i = 0;
  4309.     FOLDER_S *f;
  4310.     char     *fname;
  4311.  
  4312.     while(f = folder_entry(i, flist)){
  4313.     fname = FLDR_NAME(f);
  4314. #if defined(DOS) || defined(OS2)
  4315.     if(toupper((unsigned char)(*name))
  4316.         == toupper((unsigned char)(*fname)) && strucmp(name, fname) == 0)
  4317. #else
  4318.     if(*name == *fname && strcmp(name, fname) == 0)
  4319. #endif
  4320.       return(i);
  4321.     else
  4322.       i++;
  4323.     }
  4324.  
  4325.     return(-1);
  4326. }
  4327.  
  4328.  
  4329.  
  4330. /*
  4331.  * next_folder - given a current folder in a context, return the next in
  4332.  *               the list, or NULL if no more or there's a problem.
  4333.  */
  4334. char *
  4335. next_folder(stream, next, current, cntxt, find_recent)
  4336.     MAILSTREAM **stream;
  4337.     char    *current, *next;
  4338.     CONTEXT_S    *cntxt;
  4339.     int         find_recent;
  4340. {
  4341.     int       index, are_recent = 0;
  4342.     FOLDER_S *f = NULL;
  4343.  
  4344.     /* note: find_folders may assign "stream" */
  4345.     find_folders_in_context(stream, cntxt, NULL);
  4346.     for(index = folder_index(current, cntxt->folders) + 1;
  4347.     index > 0
  4348.     && index < folder_total(cntxt->folders)
  4349.     && (f = folder_entry(index, cntxt->folders));
  4350.     index++)
  4351.       if(find_recent){
  4352.       /* if we can't use this stream, close it up */
  4353.       if(stream && *stream
  4354.          && !context_same_stream(cntxt->context, f->name, *stream)){
  4355.           mail_close(*stream);
  4356.           *stream = NULL;
  4357.       }
  4358.  
  4359.       if(are_recent = folder_has_recent(stream, cntxt,f))
  4360.         break;
  4361.       }
  4362.  
  4363.     if(f && (!find_recent || are_recent))
  4364.       strcpy(next, FLDR_NAME(f));
  4365.     else
  4366.       *next = '\0';
  4367.  
  4368.     /* BUG: how can this be made smarter so we cache the list? */
  4369.     free_folders_in_context(cntxt);
  4370.     return((*next) ? next : NULL);
  4371. }
  4372.  
  4373.  
  4374.  
  4375. /*
  4376.  * folder_is_nick - check to see if the given name is a nickname
  4377.  *                  for some folder in the given context...
  4378.  *
  4379.  *  NOTE: no need to check if find_folder_names has been done as 
  4380.  *        nicknames can only be set by configuration...
  4381.  */
  4382. char *
  4383. folder_is_nick(nickname, flist)
  4384.     char *nickname;
  4385.     void *flist;
  4386. {
  4387.     register  int  i = 0;
  4388.     FOLDER_S *f;
  4389.  
  4390.     while(f = folder_entry(i, flist)){
  4391.     if(f->nickname && STRCMP(nickname, f->nickname) == 0)
  4392.       return(f->name);
  4393.     else
  4394.       i++;
  4395.     }
  4396.  
  4397.     return(NULL);
  4398. }
  4399.  
  4400.  
  4401.  
  4402. /*----------------------------------------------------------------------
  4403.   Insert the given folder name into the sorted folder list
  4404.   associated with the given context.  Only allow ambiguous folder
  4405.   names IF associated with a nickname.
  4406.  
  4407.    Args: index  -- Index to insert at, OR insert in sorted order if -1
  4408.          folder -- folder structure to insert into list
  4409.      flist  -- folder list to insert folder into
  4410.  
  4411.   **** WARNING ****
  4412.   DON'T count on the folder pointer being valid after this returns
  4413.   *** ALL FOLDER ELEMENT READS SHOULD BE THRU folder_entry() ***
  4414.  
  4415.   ----*/
  4416. int
  4417. folder_insert(index, folder, flist)
  4418.     int       index;
  4419.     FOLDER_S *folder;
  4420.     void     *flist;
  4421. {
  4422.     int i;
  4423.  
  4424.     if(index < 0){            /* add to sorted list */
  4425.     char     *sortname, *fsortname;
  4426.     FOLDER_S *f;
  4427.  
  4428.     sortname = FLDR_NAME(folder);
  4429.     index    = 0;
  4430.     while(f = folder_entry(index, flist)){
  4431.         fsortname = FLDR_NAME(f);
  4432.         if((i = compare_names(&sortname, &fsortname)) < 0)
  4433.           break;
  4434.         else if(i == 0)        /* same folder? just return index */
  4435.           return(index);
  4436.         else
  4437.           index++;
  4438.     }
  4439.     }
  4440.  
  4441.     folder_insert_index(folder, index, flist);
  4442.     return(index);
  4443. }
  4444.  
  4445.  
  4446. /* 
  4447.  * folder_insert_index - Insert the given folder struct into the global list
  4448.  *                 at the given index.
  4449.  */
  4450. void
  4451. folder_insert_index(folder, index, flist)
  4452.     FOLDER_S *folder;
  4453.     int       index;
  4454.     void     *flist;
  4455. {
  4456. #ifdef    DOSXXX
  4457.     FOLDER *tmp;
  4458.  
  4459.     tmp = (FOLDER_S *)fs_get(sizeof(FOLDER_S) + (MAXFOLDER * sizeof(char)));
  4460.  
  4461.  
  4462. #else
  4463.     register FOLDER_S **flp, **iflp;
  4464.  
  4465.     /* if index is beyond size, place at end of list */
  4466.     index = min(index, ((FLIST *)flist)->used);
  4467.  
  4468.     /* grow array ? */
  4469.     if(((FLIST *)flist)->used + 1 > ((FLIST *)flist)->allocated){
  4470.     ((FLIST *)flist)->allocated += FCHUNK;
  4471.     fs_resize((void **)&(((FLIST *)flist)->folders),
  4472.           (((FLIST *)flist)->allocated) * sizeof(FOLDER_S *));
  4473.     }
  4474.  
  4475.     /* shift array left */
  4476.     iflp = &(((FOLDER_S **)((FLIST *)flist)->folders)[index]);
  4477.     for(flp = &(((FOLDER_S **)((FLIST *)flist)->folders)[((FLIST *)flist)->used]); 
  4478.     flp > iflp; flp--)
  4479.       flp[0] = flp[-1];
  4480.  
  4481.     ((FLIST *)flist)->folders[index] = folder;
  4482.     ((FLIST *)flist)->used          += 1;
  4483. #endif
  4484. }
  4485.  
  4486.  
  4487. /*----------------------------------------------------------------------
  4488.     Removes a folder at the given index in the given context's
  4489.     list.
  4490.  
  4491. Args: index -- Index in folder list of folder to be removed
  4492.       flist -- folder list
  4493.  ----*/
  4494. void
  4495. folder_delete(index, flist)
  4496.     int   index;
  4497.     void *flist;
  4498. {
  4499.     register int  i;
  4500.     FOLDER_S     *f;
  4501.  
  4502.     if(((FLIST *)flist)->used 
  4503.        && (index < 0 || index >= ((FLIST *)flist)->used))
  4504.       return;                /* bogus call! */
  4505.  
  4506.     if((f = folder_entry(index, flist))->nickname)
  4507.       fs_give((void **)&(f->nickname));
  4508.       
  4509. #ifdef    DOSXXX
  4510.     /* shift all entries after index up one.... */
  4511. #else
  4512.     fs_give((void **)&(((FLIST *)flist)->folders[index]));
  4513.     for(i = index; i < ((FLIST *)flist)->used - 1; i++)
  4514.       ((FLIST *)flist)->folders[i] = ((FLIST *)flist)->folders[i+1];
  4515.  
  4516.  
  4517.     ((FLIST *)flist)->used -= 1;
  4518. #endif
  4519. }
  4520.  
  4521.  
  4522.  
  4523. /*----------------------------------------------------------------------
  4524.     Find an entry in the folder list by matching names
  4525.   ----*/
  4526. search_folder_list(list, name)
  4527.      void *list;
  4528.      char *name;
  4529. {
  4530.     int i;
  4531.     char *n;
  4532.  
  4533.     for(i = 0; i < folder_total(list); i++) {
  4534.         n = folder_entry(i, list)->name;
  4535.         if(strucmp(name, n) == 0)
  4536.           return(1); /* Found it */
  4537.     }
  4538.     return(0);
  4539. }
  4540.  
  4541.  
  4542.  
  4543. /*----------------------------------------------------------------------
  4544.    Sort the given folder list
  4545.   ----*/
  4546. sort_folder_list(list, sort)
  4547.      void  *list;
  4548.      QSFunc sort;
  4549. {
  4550.     qsort((QSType *)(((FLIST *)list)->folders),
  4551.           (size_t) folder_total(list),
  4552.           sizeof(FOLDER_S *),
  4553.           sort);
  4554. }
  4555.     
  4556.  
  4557.  
  4558.  
  4559.  
  4560.  
  4561. static CONTEXT_S *post_cntxt = NULL;
  4562.  
  4563. /*----------------------------------------------------------------------
  4564.     Verify and canonicalize news groups names. 
  4565.     Called from the message composer
  4566.  
  4567. Args:  given_group    -- List of groups typed by user
  4568.        expanded_group -- pointer to point to expanded list, which will be
  4569.              allocated here and freed in caller.  If this is
  4570.              NULL, don't attempt to validate.
  4571.        error          -- pointer to store error message
  4572.        fcc            -- pointer to point to fcc, which will be
  4573.              allocated here and freed in caller
  4574.  
  4575. Returns:  0 if all is OK
  4576.          -1 if addresses weren't valid
  4577.  
  4578. Test the given list of newstroups against those recognized by our nntp
  4579. servers.  Testing by actually trying to open the list is much cheaper, both
  4580. in bandwidth and memory, than yanking the whole list across the wire.
  4581.   ----*/
  4582. int
  4583. news_build(given_group, expanded_group, error, fcc)
  4584.     char     *given_group,
  4585.         **expanded_group,
  4586.         **error;
  4587.     BUILDER_ARG     *fcc;
  4588. {
  4589.     char     ng_error[90], *p1, *p2, *name, *end, *ep, **server;
  4590.       /* I've got no idea where 90 comes from */
  4591.     int          expanded_len = 0, num_in_error = 0, cnt_errs, we_cancel = 0;
  4592.     MAILSTREAM  *stream = NULL;
  4593.     struct ng_list {
  4594.     char  *groupname;
  4595.     NgCacheReturns  found;
  4596.     struct ng_list *next;
  4597.     }*nglist = NULL, **ntmpp, *ntmp;
  4598. #ifdef SENDNEWS
  4599.     static int no_servers = 0;
  4600. #endif
  4601.  
  4602.  
  4603.     dprint(2, (debugfile,
  4604.     "- news_build - (%s)\n", given_group ? given_group : "nul"));
  4605.  
  4606.     if(error)
  4607.       *error = NULL;
  4608.  
  4609.     /*------ parse given entries into a list ----*/
  4610.     ntmpp = &nglist;
  4611.     for(name = given_group; *name; name = end){
  4612.  
  4613.     /* find start of next group name */
  4614.         while(*name && (isspace((unsigned char)*name) || *name == ','))
  4615.       name++;
  4616.  
  4617.     /* find end of group name */
  4618.     end = name;
  4619.     while(*end && !isspace((unsigned char)*end) && *end != ',')
  4620.       end++;
  4621.  
  4622.         if(end != name){
  4623.         *ntmpp = (struct ng_list *)fs_get(sizeof(struct ng_list));
  4624.         (*ntmpp)->next      = NULL;
  4625.         (*ntmpp)->found     = NotChecked;
  4626.             (*ntmpp)->groupname = fs_get(end - name + 1);
  4627.             strncpy((*ntmpp)->groupname, name, end - name);
  4628.             (*ntmpp)->groupname[end - name] = '\0';
  4629.         ntmpp = &(*ntmpp)->next;
  4630.         if(!expanded_group)
  4631.           break;  /* no need to continue if just doing fcc */
  4632.         }
  4633.     }
  4634.  
  4635.     /*
  4636.      * If fcc is not set or is set to default, then replace it if
  4637.      * one of the recipient rules is in effect.
  4638.      */
  4639.     if(fcc){
  4640.     if((ps_global->fcc_rule == FCC_RULE_RECIP ||
  4641.         ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
  4642.            (nglist && nglist->groupname)){
  4643.       if(fcc->tptr)
  4644.         fs_give((void **)&fcc->tptr);
  4645.  
  4646.       fcc->tptr = cpystr(nglist->groupname);
  4647.     }
  4648.     else if(!fcc->tptr) /* already default otherwise */
  4649.       fcc->tptr = cpystr(ps_global->VAR_DEFAULT_FCC);
  4650.     }
  4651.  
  4652.     if(!nglist){
  4653.     if(expanded_group)
  4654.       *expanded_group = cpystr("");
  4655.         return 0;
  4656.     }
  4657.  
  4658.     if(!expanded_group)
  4659.       return 0;
  4660.  
  4661. #ifdef    DEBUG
  4662.     for(ntmp = nglist; debug >= 9 && ntmp; ntmp = ntmp->next)
  4663.       dprint(9, (debugfile, "Parsed group: --[%s]--\n", ntmp->groupname));
  4664. #endif
  4665.  
  4666.     /* If we are doing validation */
  4667.     if(F_OFF(F_NO_NEWS_VALIDATION, ps_global)){
  4668.     int need_to_talk_to_server = 0;
  4669.  
  4670.     /*
  4671.      * First check our cache of validated newsgroups to see if we even
  4672.      * have to open a stream.
  4673.      */
  4674.     for(ntmp = nglist; ntmp; ntmp = ntmp->next){
  4675.         ntmp->found = chk_newsgrp_cache(ntmp->groupname);
  4676.         if(ntmp->found == NotInCache)
  4677.           need_to_talk_to_server++;
  4678.     }
  4679.  
  4680.     if(need_to_talk_to_server){
  4681.  
  4682. #ifdef SENDNEWS
  4683.       if(no_servers == 0)
  4684. #endif
  4685.         we_cancel = busy_alarm(1, "Validating newsgroup(s)", NULL, 1);
  4686.  
  4687.         /*
  4688.          * Build a stream to the first server that'll talk to us...
  4689.          */
  4690.         for(server = ps_global->VAR_NNTP_SERVER;
  4691.         server && *server && **server;
  4692.         server++){
  4693.         char name[MAXPATH];
  4694.  
  4695.         sprintf(name, "*{%s/nntp}", *server);
  4696.         if(stream = mail_open(stream, name, OP_HALFOPEN))
  4697.           break;
  4698.         }
  4699.  
  4700.         if(!server || !stream){
  4701.         if(error)
  4702. #ifdef SENDNEWS
  4703.         {
  4704.          /* don't say this over and over */
  4705.          if(no_servers == 0){
  4706.             if(!server || !*server || !**server)
  4707.               no_servers++;
  4708.  
  4709.             *error = cpystr(no_servers
  4710.                 ? "Can't validate groups.  No servers defined"
  4711.                 : "Can't validate groups.  No servers responding");
  4712.          }
  4713.         }
  4714. #else
  4715.           *error = cpystr((!server || !*server || !**server)
  4716.                 ? "No servers defined for posting to newsgroups"
  4717.                 : "Can't validate groups.  No servers responding");
  4718. #endif
  4719.         *expanded_group = cpystr(given_group);
  4720.         goto done;
  4721.         }
  4722.     }
  4723.  
  4724.     /*
  4725.      * Now, go thru the list, making sure we can at least open each one...
  4726.      */
  4727.     for(ntmp = nglist; ntmp; ntmp = ntmp->next){
  4728.         /*
  4729.          * It's faster and easier right now just to open the stream and
  4730.          * do our own finds than to use the current folder_exists()
  4731.          * interface...
  4732.          */
  4733.         if(ntmp->found == NotInCache){
  4734.         find_folder_list  = NULL;
  4735.         find_folder_count = 0L;
  4736.         context_find_all_bboard(NULL, stream, ntmp->groupname);
  4737.         ntmp->found = (find_folder_count > 0L) ? Found : Missing;
  4738.         }
  4739.         add_newsgrp_cache(ntmp->groupname, ntmp->found);
  4740.     }
  4741.  
  4742.     mail_close(stream);
  4743.     }
  4744.  
  4745.     /* figure length of string for matching groups */
  4746.     for(ntmp = nglist; ntmp; ntmp = ntmp->next){
  4747.       if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global))
  4748.     expanded_len += strlen(ntmp->groupname) + 2;
  4749.       else
  4750.     num_in_error++;
  4751.     }
  4752.  
  4753.     /*
  4754.      * allocate and write the allowed, and error lists...
  4755.      */
  4756.     p1 = *expanded_group = fs_get((expanded_len + 1) * sizeof(char));
  4757.     if(error && num_in_error){
  4758.     cnt_errs = num_in_error;
  4759.     memset((void *)ng_error, 0, (size_t)90);
  4760.     sprintf(ng_error, "Unknown news group%s: ", plural(num_in_error));
  4761.     ep = ng_error + strlen(ng_error);
  4762.     }
  4763.     for(ntmp = nglist; ntmp; ntmp = ntmp->next){
  4764.     p2 = ntmp->groupname;
  4765.     if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global)){
  4766.         while(*p2)
  4767.           *p1++ = *p2++;
  4768.  
  4769.         if(ntmp->next){
  4770.         *p1++ = ',';
  4771.         *p1++ = ' ';
  4772.         }
  4773.     }
  4774.     else if (error){
  4775.         while(*p2 && (ep - ng_error < 89))
  4776.           *ep++ = *p2++;
  4777.  
  4778.         if(--cnt_errs > 0 && (ep - ng_error < 87)){
  4779.         strcpy(ep, ", ");
  4780.         ep += 2;
  4781.         }
  4782.     }
  4783.     }
  4784.  
  4785.     *p1 = '\0';
  4786.  
  4787.     if(error && num_in_error)
  4788.       *error = cpystr(ng_error);
  4789.  
  4790. done:
  4791.     while(ntmp = nglist){
  4792.     nglist = nglist->next;
  4793.     fs_give((void **)&ntmp->groupname);
  4794.     fs_give((void **)&ntmp);
  4795.     }
  4796.  
  4797.     if(we_cancel)
  4798.       cancel_busy_alarm(0);
  4799.  
  4800.     return(num_in_error ? -1 : 0);
  4801. }
  4802.  
  4803.  
  4804. typedef struct ng_cache {
  4805.     char          *name;
  4806.     NgCacheReturns val;
  4807. }NgCache;
  4808. static NgCache *ng_cache_ptr;
  4809. #if defined(DOS) && !defined(_WINDOWS)
  4810. #define MAX_NGCACHE_ENTRIES 15
  4811. #else
  4812. #define MAX_NGCACHE_ENTRIES 40
  4813. #endif
  4814. /*
  4815.  * Simple newsgroup validity cache.  Opening a newsgroup to see if it
  4816.  * exists can be very slow on a heavily loaded NNTP server, so we cache
  4817.  * the results.
  4818.  */
  4819. NgCacheReturns
  4820. chk_newsgrp_cache(group)
  4821. char *group;
  4822. {
  4823.     register NgCache *ngp;
  4824.     
  4825.     for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
  4826.     if(strcmp(group, ngp->name) == 0)
  4827.       return(ngp->val);
  4828.     }
  4829.  
  4830.     return NotInCache;
  4831. }
  4832.  
  4833.  
  4834. /*
  4835.  * Add an entry to the newsgroup validity cache.
  4836.  *
  4837.  * LRU entry is the one on the bottom, oldest on the top.
  4838.  * A slot has an entry in it if name is not NULL.
  4839.  */
  4840. void
  4841. add_newsgrp_cache(group, result)
  4842. char *group;
  4843. NgCacheReturns result;
  4844. {
  4845.     register NgCache *ngp;
  4846.     NgCache save_ngp;
  4847.  
  4848.     /* first call, initialize cache */
  4849.     if(!ng_cache_ptr){
  4850.     int i;
  4851.  
  4852.     ng_cache_ptr =
  4853.         (NgCache *)fs_get((MAX_NGCACHE_ENTRIES+1)*sizeof(NgCache));
  4854.     for(i = 0; i <= MAX_NGCACHE_ENTRIES; i++){
  4855.         ng_cache_ptr[i].name = NULL;
  4856.         ng_cache_ptr[i].val  = NotInCache;
  4857.     }
  4858.     ng_cache_ptr[MAX_NGCACHE_ENTRIES].val  = End;
  4859.     }
  4860.  
  4861.     if(chk_newsgrp_cache(group) == NotInCache){
  4862.     /* find first empty slot or End */
  4863.     for(ngp = ng_cache_ptr; ngp->name; ngp++)
  4864.       ;/* do nothing */
  4865.     if(ngp->val == End){
  4866.         /*
  4867.          * Cache is full, throw away top entry, move everything up,
  4868.          * and put new entry on the bottom.
  4869.          */
  4870.         ngp = ng_cache_ptr;
  4871.         if(ngp->name) /* just making sure */
  4872.           fs_give((void **)&ngp->name);
  4873.  
  4874.         for(; (ngp+1)->name; ngp++){
  4875.         ngp->name = (ngp+1)->name;
  4876.         ngp->val  = (ngp+1)->val;
  4877.         }
  4878.     }
  4879.     ngp->name = cpystr(group);
  4880.     ngp->val  = result;
  4881.     }
  4882.     else{
  4883.     /*
  4884.      * Move this entry from current location to last to preserve
  4885.      * LRU order.
  4886.      */
  4887.     for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
  4888.         if(strcmp(group, ngp->name) == 0) /* found it */
  4889.           break;
  4890.     }
  4891.     save_ngp.name = ngp->name;
  4892.     save_ngp.val  = ngp->val;
  4893.     for(; (ngp+1)->name; ngp++){
  4894.         ngp->name = (ngp+1)->name;
  4895.         ngp->val  = (ngp+1)->val;
  4896.     }
  4897.     ngp->name = save_ngp.name;
  4898.     ngp->val  = save_ngp.val;
  4899.     }
  4900. }
  4901.  
  4902.  
  4903. void
  4904. free_newsgrp_cache()
  4905. {
  4906.     register NgCache *ngp;
  4907.  
  4908.     for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++)
  4909.       fs_give((void **)&ngp->name);
  4910.     if(ng_cache_ptr)
  4911.       fs_give((void **)&ng_cache_ptr);
  4912. }
  4913.  
  4914.  
  4915. /*----------------------------------------------------------------------
  4916.     Browse list of newsgroups available for posting
  4917.  
  4918.   Called from composer when ^T is typed in newsgroups field
  4919.  
  4920. Args:    none
  4921.  
  4922. Returns: pointer to selected newsgroup, or NULL.
  4923.          Selector call in composer expects this to be alloc'd here.
  4924.  
  4925.   ----*/
  4926. char *
  4927. news_group_selector(error_mess)
  4928.     char **error_mess;
  4929. {
  4930.     FSTATE_S  post_state, *push_state;
  4931.     char     *post_folder;
  4932.     int       rc;
  4933.     char     *em;
  4934.  
  4935.     /* Coming back from composer */
  4936.     fix_windsize(ps_global);
  4937.     init_sigwinch();
  4938.  
  4939.     post_folder = fs_get((size_t)500);
  4940.  
  4941.     /*--- build the post_cntxt -----*/
  4942.     em = get_post_list(ps_global->VAR_NNTP_SERVER);
  4943.     if(em != NULL){
  4944.         if(error_mess != NULL)
  4945.           *error_mess = em;
  4946.  
  4947.     cancel_busy_alarm(-1);
  4948.         return(NULL);
  4949.     }
  4950.  
  4951.     /*----- Call the browser -------*/
  4952.     push_state = fs;
  4953.     rc = folder_lister(ps_global, PostNews, post_cntxt, NULL,
  4954.                        post_folder, NULL, post_cntxt, &post_state);
  4955.     fs = push_state;
  4956.  
  4957.     cancel_busy_alarm(-1);
  4958.  
  4959.     if(rc <= 0)
  4960.       return(NULL);
  4961.  
  4962.     return(post_folder);
  4963. }
  4964.  
  4965.  
  4966.  
  4967. /*----------------------------------------------------------------------
  4968.     Get the list of news groups that are possible for posting
  4969.  
  4970. Args: post_host -- host name for posting
  4971.  
  4972. Returns NULL if list is retrieved, pointer to error message if failed
  4973.  
  4974. This is kept in a standards "CONTEXT" for a acouple of reasons. First
  4975. it makes it very easy to use the folder browser to display the
  4976. newsgroup for selection on ^T from the composer. Second it will allow
  4977. the same mechanism to be used for all folder lists on memory tight
  4978. systems like DOS. The list is kept for the life of the session because
  4979. fetching it is a expensive. 
  4980.  
  4981.  ----*/
  4982. char *
  4983. get_post_list(post_host)
  4984.      char **post_host;
  4985. {
  4986.     char *post_context_string;
  4987.  
  4988.     if(!post_host || !post_host[0]) {
  4989.         /* BUG should assume inews and get this from active file */
  4990.         return("Can't post messages, NNTP server needs to be configured");
  4991.     }
  4992.  
  4993.     if(!post_cntxt){
  4994.     int we_cancel;
  4995.  
  4996.     we_cancel
  4997.         = busy_alarm(1, "Getting full list of groups for posting", NULL, 0);
  4998.  
  4999.         post_context_string = fs_get(strlen(post_host[0]) + 20);
  5000.         sprintf(post_context_string, "*{%s/nntp}[]", post_host[0]);
  5001.         
  5002.         post_cntxt          = new_context(post_context_string);
  5003.  
  5004.         post_cntxt->use    |= CNTXT_FINDALL | CNTXT_NOFIND; 
  5005.         post_cntxt->next    = NULL;
  5006.  
  5007.         find_folders_in_context(NULL, post_cntxt, NULL);
  5008.     if(we_cancel)
  5009.       cancel_busy_alarm(-1);
  5010.     }
  5011.     return(NULL);
  5012. }
  5013.  
  5014.  
  5015. #ifdef _WINDOWS
  5016. /*----------------------------------------------------------------------
  5017.      MSWin scroll callback.  Called during scroll message processing.
  5018.          
  5019.  
  5020.  
  5021.   Args: cmd - what type of scroll operation.
  5022.     scroll_pos - paramter for operation.  
  5023.             used as position for SCROLL_TO operation.
  5024.  
  5025.   Returns: TRUE - did the scroll operation.
  5026.        FALSE - was not able to do the scroll operation.
  5027.  ----*/
  5028. int
  5029. folder_scroll_callback (cmd, scroll_pos)
  5030. int    cmd;
  5031. long    scroll_pos;
  5032. {
  5033.     static short bad_timing = 0;
  5034.     int    rv;
  5035.  
  5036.     if(bad_timing)
  5037.       return(FALSE);
  5038.     else
  5039.       bad_timing = TRUE;
  5040.  
  5041.     switch (cmd) {
  5042.       case MSWIN_KEY_SCROLLUPLINE:
  5043.     rv = folder_scroll_down(1L);
  5044.     break;
  5045.  
  5046.       case MSWIN_KEY_SCROLLDOWNLINE:
  5047.     rv = folder_scroll_up(1L);
  5048.     break;
  5049.  
  5050.       case MSWIN_KEY_SCROLLUPPAGE:
  5051.     rv = folder_scroll_down(fs->display_rows);
  5052.     break;
  5053.  
  5054.       case MSWIN_KEY_SCROLLDOWNPAGE:
  5055.     rv = folder_scroll_up(fs->display_rows);
  5056.     break;
  5057.  
  5058.       case MSWIN_KEY_SCROLLTO:
  5059.     rv =  folder_scroll_to_pos(scroll_pos);
  5060.     break;
  5061.     }
  5062.  
  5063.     if(rv)
  5064.       display_folder(fs, fs->context, fs->folder_index, NULL, -1);
  5065.  
  5066.     bad_timing = FALSE;
  5067.     return(rv);
  5068. }
  5069. #endif    /* _WINDOWS */
  5070.